From 80c0b4ab6ddb26204fddc4c93831d974125a6418 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 10 Dec 2019 06:26:55 +0100 Subject: [PATCH 001/529] Ubuntu font: move to a separate directory - To include the font's copyright and license files as required by the license Signed-off-by: Michael Opdenacker --- http/fonts.css | 36 ++-- http/fonts/ubuntu/LICENCE-FAQ.txt | 177 ++++++++++++++++++ http/fonts/ubuntu/LICENCE.txt | 96 ++++++++++ http/fonts/ubuntu/copyright.txt | 5 + ...n_latin-ext_greek-ext_cyrillic-ext-700.eot | Bin ...n_latin-ext_greek-ext_cyrillic-ext-700.svg | 0 ...n_latin-ext_greek-ext_cyrillic-ext-700.ttf | Bin ..._latin-ext_greek-ext_cyrillic-ext-700.woff | Bin ...latin-ext_greek-ext_cyrillic-ext-700.woff2 | Bin ...atin-ext_greek-ext_cyrillic-ext-italic.eot | Bin ...atin-ext_greek-ext_cyrillic-ext-italic.svg | 0 ...atin-ext_greek-ext_cyrillic-ext-italic.ttf | Bin ...tin-ext_greek-ext_cyrillic-ext-italic.woff | Bin ...in-ext_greek-ext_cyrillic-ext-italic.woff2 | Bin ...tin-ext_greek-ext_cyrillic-ext-regular.eot | Bin ...tin-ext_greek-ext_cyrillic-ext-regular.svg | 0 ...tin-ext_greek-ext_cyrillic-ext-regular.ttf | Bin ...in-ext_greek-ext_cyrillic-ext-regular.woff | Bin ...n-ext_greek-ext_cyrillic-ext-regular.woff2 | Bin 19 files changed, 296 insertions(+), 18 deletions(-) create mode 100644 http/fonts/ubuntu/LICENCE-FAQ.txt create mode 100644 http/fonts/ubuntu/LICENCE.txt create mode 100644 http/fonts/ubuntu/copyright.txt rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2 (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2 (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff (100%) rename http/fonts/{ => ubuntu}/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2 (100%) diff --git a/http/fonts.css b/http/fonts.css index 0f74b77d..eafd55d7 100644 --- a/http/fonts.css +++ b/http/fonts.css @@ -3,37 +3,37 @@ font-family: 'Ubuntu'; font-style: normal; font-weight: 400; - src: url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot'); /* IE9 Compat Modes */ + src: url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot'); /* IE9 Compat Modes */ src: local('Ubuntu Regular'), local('Ubuntu-Regular'), - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2') format('woff2'), /* Super Modern Browsers */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff') format('woff'), /* Modern Browsers */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf') format('truetype'), /* Safari, Android, iOS */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg#Ubuntu') format('svg'); /* Legacy iOS */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2') format('woff2'), /* Super Modern Browsers */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff') format('woff'), /* Modern Browsers */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf') format('truetype'), /* Safari, Android, iOS */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg#Ubuntu') format('svg'); /* Legacy iOS */ } /* ubuntu-italic - cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext */ @font-face { font-family: 'Ubuntu'; font-style: italic; font-weight: 400; - src: url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot'); /* IE9 Compat Modes */ + src: url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot'); /* IE9 Compat Modes */ src: local('Ubuntu Italic'), local('Ubuntu-Italic'), - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2') format('woff2'), /* Super Modern Browsers */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff') format('woff'), /* Modern Browsers */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf') format('truetype'), /* Safari, Android, iOS */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg#Ubuntu') format('svg'); /* Legacy iOS */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2') format('woff2'), /* Super Modern Browsers */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff') format('woff'), /* Modern Browsers */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf') format('truetype'), /* Safari, Android, iOS */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg#Ubuntu') format('svg'); /* Legacy iOS */ } /* ubuntu-700 - cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext */ @font-face { font-family: 'Ubuntu'; font-style: normal; font-weight: 700; - src: url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot'); /* IE9 Compat Modes */ + src: url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot'); /* IE9 Compat Modes */ src: local('Ubuntu Bold'), local('Ubuntu-Bold'), - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2') format('woff2'), /* Super Modern Browsers */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff') format('woff'), /* Modern Browsers */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf') format('truetype'), /* Safari, Android, iOS */ - url('/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg#Ubuntu') format('svg'); /* Legacy iOS */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2') format('woff2'), /* Super Modern Browsers */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff') format('woff'), /* Modern Browsers */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf') format('truetype'), /* Safari, Android, iOS */ + url('/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg#Ubuntu') format('svg'); /* Legacy iOS */ } diff --git a/http/fonts/ubuntu/LICENCE-FAQ.txt b/http/fonts/ubuntu/LICENCE-FAQ.txt new file mode 100644 index 00000000..776a25ed --- /dev/null +++ b/http/fonts/ubuntu/LICENCE-FAQ.txt @@ -0,0 +1,177 @@ + Ubuntu Font Family Licensing FAQ + + Stylistic Foundations + + The Ubuntu Font Family is the first time that a libre typeface has been + designed professionally and explicitly with the intent of developing a + public and long-term community-based development process. + + When developing an open project, it is generally necessary to have firm + foundations: a font needs to maintain harmony within itself even across + many type designers and writing systems. For the [1]Ubuntu Font Family, + the process has been guided with the type foundry Dalton Maag setting + the project up with firm stylistic foundation covering several + left-to-right scripts: Latin, Greek and Cyrillic; and right-to-left + scripts: Arabic and Hebrew (due in 2011). + + With this starting point the community will, under the supervision of + [2]Canonical and [3]Dalton Maag, be able to build on the existing font + sources to expand their character coverage. Ultimately everybody will + be able to use the Ubuntu Font Family in their own written languages + across the whole of Unicode (and this will take some time!). + + Licensing + + The licence chosen by any free software project is one of the + foundational decisions that sets out how derivatives and contributions + can occur, and in turn what kind of community will form around the + project. + + Using a licence that is compatible with other popular licences is a + powerful constraint because of the [4]network effects: the freedom to + share improvements between projects allows free software to reach + high-quality over time. Licence-proliferation leads to many + incompatible licences, undermining the network effect, the freedom to + share and ultimately making the libre movement that Ubuntu is a part of + less effective. For all kinds of software, writing a new licence is not + to be taken lightly and is a choice that needs to be thoroughly + justified if this path is taken. + + Today it is not clear to Canonical what the best licence for a font + project like the Ubuntu Font Family is: one that starts life designed + by professionals and continues with the full range of community + development, from highly commercial work in new directions to curious + beginners' experimental contributions. The fast and steady pace of the + Ubuntu release cycle means that an interim libre licence has been + necessary to enable the consideration of the font family as part of + Ubuntu 10.10 operating system release. + + Before taking any decision on licensing, Canonical as sponsor and + backer of the project has reviewed the many existing licenses used for + libre/open fonts and engaged the stewards of the most popular licenses + in detailed discussions. The current interim licence is the first step + in progressing the state-of-the-art in licensing for libre/open font + development. + + The public discussion must now involve everyone in the (comparatively + new) area of the libre/open font community; including font users, + software freedom advocates, open source supporters and existing libre + font developers. Most importantly, the minds and wishes of professional + type designers considering entering the free software business + community must be taken on board. + + Conversations and discussion has taken place, privately, with + individuals from the following groups (generally speaking personally on + behalf of themselves, rather than their affiliations): + * [5]SIL International + * [6]Open Font Library + * [7]Software Freedom Law Center + * [8]Google Font API + + Document embedding + + One issue highlighted early on in the survey of existing font licences + is that of document embedding. Almost all font licences, both free and + unfree, permit embedding a font into a document to a certain degree. + Embedding a font with other works that make up a document creates a + "combined work" and copyleft would normally require the whole document + to be distributed under the terms of the font licence. As beautiful as + the font might be, such a licence makes a font too restrictive for + useful general purpose digital publishing. + + The situation is not entirely unique to fonts and is encountered also + with tools such as GNU Bison: a vanilla GNU GPL licence would require + anything generated with Bison to be made available under the terms of + the GPL as well. To avoid this, Bison is [9]published with an + additional permission to the GPL which allows the output of Bison to be + made available under any licence. + + The conflict between licensing of fonts and licensing of documents, is + addressed in two popular libre font licences, the SIL OFL and GNU GPL: + * [10]SIL Open Font Licence: When OFL fonts are embedded in a + document, the OFL's terms do not apply to that document. (See + [11]OFL-FAQ for details. + * [12]GPL Font Exception: The situation is resolved by granting an + additional permission to allow documents to not be covered by the + GPL. (The exception is being reviewed). + + The Ubuntu Font Family must also resolve this conflict, ensuring that + if the font is embedded and then extracted it is once again clearly + under the terms of its libre licence. + + Long-term licensing + + Those individuals involved, especially from Ubuntu and Canonical, are + interested in finding a long-term libre licence that finds broad favour + across the whole libre/open font community. The deliberation during the + past months has been on how to licence the Ubuntu Font Family in the + short-term, while knowingly encouraging everyone to pursue a long-term + goal. + * [13]Copyright assignment will be required so that the Ubuntu Font + Family's licensing can be progressively expanded to one (or more) + licences, as best practice continues to evolve within the + libre/open font community. + * Canonical will support and fund legal work on libre font licensing. + It is recognised that the cost and time commitments required are + likely to be significant. We invite other capable parties to join + in supporting this activity. + + The GPL version 3 (GPLv3) will be used for Ubuntu Font Family build + scripts and the CC-BY-SA for associated documentation and non-font + content: all items which do not end up embedded in general works and + documents. + +Ubuntu Font Licence + + For the short-term only, the initial licence is the [14]Ubuntu Font + License (UFL). This is loosely inspired from the work on the SIL + OFL 1.1, and seeks to clarify the issues that arose during discussions + and legal review, from the perspective of the backers, Canonical Ltd. + Those already using established licensing models such as the GPL, OFL + or Creative Commons licensing should have no worries about continuing + to use them. The Ubuntu Font Licence (UFL) and the SIL Open Font + Licence (SIL OFL) are not identical and should not be confused with + each other. Please read the terms precisely. The UFL is only intended + as an interim license, and the overriding aim is to support the + creation of a more suitable and generic libre font licence. As soon as + such a licence is developed, the Ubuntu Font Family will migrate to + it—made possible by copyright assignment in the interium. Between the + OFL 1.1, and the UFL 1.0, the following changes are made to produce the + Ubuntu Font Licence: + * Clarification: + + 1. Document embedding (see [15]embedding section above). + 2. Apply at point of distribution, instead of receipt + 3. Author vs. copyright holder disambiguation (type designers are + authors, with the copyright holder normally being the funder) + 4. Define "Propagate" (for internationalisation, similar to the GPLv3) + 5. Define "Substantially Changed" + 6. Trademarks are explicitly not transferred + 7. Refine renaming requirement + + Streamlining: + 8. Remove "not to be sold separately" clause + 9. Remove "Reserved Font Name(s)" declaration + + A visual demonstration of how these points were implemented can be + found in the accompanying coloured diff between SIL OFL 1.1 and the + Ubuntu Font Licence 1.0: [16]ofl-1.1-ufl-1.0.diff.html + +References + + 1. http://font.ubuntu.com/ + 2. http://www.canonical.com/ + 3. http://www.daltonmaag.com/ + 4. http://en.wikipedia.org/wiki/Network_effect + 5. http://scripts.sil.org/ + 6. http://openfontlibrary.org/ + 7. http://www.softwarefreedom.org/ + 8. http://code.google.com/webfonts + 9. http://www.gnu.org/licenses/gpl-faq.html#CanIUseGPLToolsForNF + 10. http://scripts.sil.org/OFL_web + 11. http://scripts.sil.org/OFL-FAQ_web + 12. http://www.gnu.org/licenses/gpl-faq.html#FontException + 13. https://launchpad.net/~uff-contributors + 14. http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt + 15. http://font.ubuntu.com/ufl/FAQ.html#embedding + 16. http://font.ubuntu.com/ufl/ofl-1.1-ufl-1.0.diff.html diff --git a/http/fonts/ubuntu/LICENCE.txt b/http/fonts/ubuntu/LICENCE.txt new file mode 100644 index 00000000..ae78a8f9 --- /dev/null +++ b/http/fonts/ubuntu/LICENCE.txt @@ -0,0 +1,96 @@ +------------------------------- +UBUNTU FONT LICENCE Version 1.0 +------------------------------- + +PREAMBLE +This licence allows the licensed fonts to be used, studied, modified and +redistributed freely. The fonts, including any derivative works, can be +bundled, embedded, and redistributed provided the terms of this licence +are met. The fonts and derivatives, however, cannot be released under +any other licence. The requirement for fonts to remain under this +licence does not require any document created using the fonts or their +derivatives to be published under this licence, as long as the primary +purpose of the document is not to be a vehicle for the distribution of +the fonts. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this licence and clearly marked as such. This may +include source files, build scripts and documentation. + +"Original Version" refers to the collection of Font Software components +as received under this licence. + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to +a new environment. + +"Copyright Holder(s)" refers to all individuals and companies who have a +copyright ownership of the Font Software. + +"Substantially Changed" refers to Modified Versions which can be easily +identified as dissimilar to the Font Software by users of the Font +Software comparing the Original Version with the Modified Version. + +To "Propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification and with or without charging +a redistribution fee), making available to the public, and in some +countries other activities as well. + +PERMISSION & CONDITIONS +This licence does not grant any rights under trademark law and all such +rights are reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to propagate the Font Software, subject to +the below conditions: + +1) Each copy of the Font Software must contain the above copyright +notice and this licence. These can be included either as stand-alone +text files, human-readable headers or in the appropriate machine- +readable metadata fields within text or binary files as long as those +fields can be easily viewed by the user. + +2) The font name complies with the following: +(a) The Original Version must retain its name, unmodified. +(b) Modified Versions which are Substantially Changed must be renamed to +avoid use of the name of the Original Version or similar names entirely. +(c) Modified Versions which are not Substantially Changed must be +renamed to both (i) retain the name of the Original Version and (ii) add +additional naming elements to distinguish the Modified Version from the +Original Version. The name of such Modified Versions must be the name of +the Original Version, with "derivative X" where X represents the name of +the new work, appended to that name. + +3) The name(s) of the Copyright Holder(s) and any contributor to the +Font Software shall not be used to promote, endorse or advertise any +Modified Version, except (i) as required by this licence, (ii) to +acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with +their explicit written permission. + +4) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this licence, and must not be distributed +under any other licence. The requirement for fonts to remain under this +licence does not affect any document created using the Font Software, +except any version of the Font Software extracted from a document +created using the Font Software may only be distributed under this +licence. + +TERMINATION +This licence becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. diff --git a/http/fonts/ubuntu/copyright.txt b/http/fonts/ubuntu/copyright.txt new file mode 100644 index 00000000..77340703 --- /dev/null +++ b/http/fonts/ubuntu/copyright.txt @@ -0,0 +1,5 @@ +Copyright 2010,2011 Canonical Ltd. + +This Font Software is licensed under the Ubuntu Font Licence, Version +1.0. https://launchpad.net/ubuntu-font-licence + diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.eot diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.svg diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.ttf diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2 b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2 similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2 rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-700.woff2 diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.eot diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.svg diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.ttf diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2 b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2 similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2 rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-italic.woff2 diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.eot diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.svg diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.ttf diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff diff --git a/http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2 b/http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2 similarity index 100% rename from http/fonts/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2 rename to http/fonts/ubuntu/ubuntu-v14-cyrillic_greek_latin_latin-ext_greek-ext_cyrillic-ext-regular.woff2 From 6fcfc0ca79ffe6979431a9567bf6471ac47ad9b5 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Sun, 15 Dec 2019 15:15:09 -0500 Subject: [PATCH 002/529] Fixed path dependency in lib.py --- api/api.py | 13 ++----------- http/web.py | 22 +++++++--------------- lib.py | 4 +++- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/api/api.py b/api/api.py index 50add918..0edd0779 100755 --- a/api/api.py +++ b/api/api.py @@ -15,15 +15,6 @@ def build_query(env, project): import query return query.query -def call_query(query, *args): - cwd = os.getcwd() - os.chdir(ELIXIR_DIR) - ret = query(*args) - os.chdir(cwd) - - return ret - - class IdentResource: def on_get(self, req, resp, project, ident): query = build_query(req.env, project) @@ -33,9 +24,9 @@ def on_get(self, req, resp, project, ident): raise falcon.HTTPMissingParam('version') if version == 'latest': - version = call_query(query, 'latest') + version = query('latest') - symbol_definitions, symbol_references = call_query(query, 'ident', version, ident) + symbol_definitions, symbol_references = query('ident', version, ident) if len(symbol_definitions) or len(symbol_references): resp.body = json.dumps( { diff --git a/http/web.py b/http/web.py index 3ecb33c3..c94e1e74 100755 --- a/http/web.py +++ b/http/web.py @@ -106,18 +106,10 @@ def print(arg, end='\n'): import sys sys.path = [ sys.path[0] + '/..' ] + sys.path -import query - -def call_query(*args): - cwd = os.getcwd() - os.chdir('..') - ret = query.query(*args) - os.chdir(cwd) - - return ret +from query import query if version == 'latest': - tag = call_query('latest') + tag = query('latest') else: tag = version @@ -132,7 +124,7 @@ def call_query(*args): 'breadcrumb': '/' } -versions = call_query('versions') +versions = query('versions') v = '' b = 1 @@ -175,12 +167,12 @@ def call_query(*args): lines = ['null - -'] - type = call_query('type', tag, path) + type = query('type', tag, path) if len(type) > 0: if type == 'tree': - lines += call_query('dir', tag, path) + lines += query('dir', tag, path) elif type == 'blob': - code = call_query('file', tag, path) + code = query('file', tag, path) else: print('

This file does not exist.

') status = 404 @@ -276,7 +268,7 @@ def call_query(*args): elif mode == 'ident': data['title'] = project.capitalize()+' source code: '+ident+' identifier ('+tag+') - Bootlin' - symbol_definitions, symbol_references = call_query('ident', tag, ident) + symbol_definitions, symbol_references = query('ident', tag, ident) print('
') if len(symbol_definitions): diff --git a/lib.py b/lib.py index 02a869c6..a768fd65 100755 --- a/lib.py +++ b/lib.py @@ -20,8 +20,10 @@ import subprocess, os +CURRNET_DIR = os.path.dirname(os.path.abspath(__file__)) + def script(*args): - args = ('./script.sh',) + args + args = (os.path.join(CURRNET_DIR, 'script.sh'),) + args # subprocess.run was introduced in Python 3.5 # fall back to subprocess.check_output if it's not available if hasattr(subprocess, 'run'): From c1af3013902539c6dc87c91a688630789aac2f09 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 17 Dec 2019 10:39:39 +0100 Subject: [PATCH 003/529] Fix variable name in previous commit - Most probably a typo Signed-off-by: Michael Opdenacker --- lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib.py b/lib.py index a768fd65..42b1c45b 100755 --- a/lib.py +++ b/lib.py @@ -20,10 +20,10 @@ import subprocess, os -CURRNET_DIR = os.path.dirname(os.path.abspath(__file__)) +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) def script(*args): - args = (os.path.join(CURRNET_DIR, 'script.sh'),) + args + args = (os.path.join(CURRENT_DIR, 'script.sh'),) + args # subprocess.run was introduced in Python 3.5 # fall back to subprocess.check_output if it's not available if hasattr(subprocess, 'run'): From f4129147ade2e13684fcb023a4a34d70703a1841 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 17 Dec 2019 15:45:42 +0100 Subject: [PATCH 004/529] Blacklist "else" and "endif" - This fixes https://github.com/bootlin/elixir/issues/76 Signed-off-by: Michael Opdenacker --- lib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib.py b/lib.py index 42b1c45b..07745cc0 100755 --- a/lib.py +++ b/lib.py @@ -58,6 +58,8 @@ def unescape(bstr): blacklist = ( b'if', + b'else', + b'endif', b'dev', b'i', b'ret', From 952329f32b16743d0d03d125626b4d0a264feb87 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 18 Dec 2019 10:13:15 +0100 Subject: [PATCH 005/529] Fix http error handling - Return "400" status when the project doesn't have a database or repo - In case the DB is not found, properly print the corresponding file name in the logs Signed-off-by: Michael Opdenacker --- data.py | 4 +++- http/web.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/data.py b/data.py index 96c28716..2e85ec04 100755 --- a/data.py +++ b/data.py @@ -22,7 +22,9 @@ from io import BytesIO import re from lib import autoBytes +import os import os.path +import errno ################################################################################## @@ -155,7 +157,7 @@ def __init__(self, dir, readonly=True): if os.path.isdir(dir): self.dir = dir else: - raise FileNotFoundError + raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), dir) ro = readonly diff --git a/http/web.py b/http/web.py index c94e1e74..db0ffbac 100755 --- a/http/web.py +++ b/http/web.py @@ -82,6 +82,13 @@ def print(arg, end='\n'): else: status = 400 +basedir = os.environ['LXR_PROJ_DIR'] +datadir = basedir + '/' + project + '/data'; +repodir = basedir + '/' + project + '/repo'; + +if not(os.path.exists(datadir)) or not(os.path.exists(repodir)): + status = 400 + if status == 301: realprint('Status: 301 Moved Permanently') realprint('Location: '+location+'\n') @@ -94,9 +101,8 @@ def print(arg, end='\n'): realprint('Status: 400 Bad Request\n') exit() -basedir = os.environ['LXR_PROJ_DIR'] -os.environ['LXR_DATA_DIR'] = basedir + '/' + project + '/data'; -os.environ['LXR_REPO_DIR'] = basedir + '/' + project + '/repo'; +os.environ['LXR_DATA_DIR'] = datadir +os.environ['LXR_REPO_DIR'] = repodir projects = [] for (dirpath, dirnames, filenames) in os.walk(basedir): From a66996d3987a9b30a0755338e582ee071c1cac7d Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 18 Dec 2019 16:11:53 +0100 Subject: [PATCH 006/529] http/web.py: improve code readability and fix issue - Fix issue introduced by the previous commit The 'project' variable could be tested even in the case it wasn't defined (case of an invalid URL) Signed-off-by: Michael Opdenacker --- http/web.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/http/web.py b/http/web.py index db0ffbac..f6900f8e 100755 --- a/http/web.py +++ b/http/web.py @@ -48,12 +48,22 @@ def print(arg, end='\n'): version = m.group(2) cmd = m.group(3) arg = m.group(4) + + # Support old LXR links if not(project and search('^[A-Za-z0-9-]+$', project)) \ or not(version and search('^[A-Za-z0-9._-]+$', version)): status = 302 location = '/linux/latest/'+cmd+arg cmd = '' - if cmd == 'source': + + basedir = os.environ['LXR_PROJ_DIR'] + datadir = basedir + '/' + project + '/data' + repodir = basedir + '/' + project + '/repo' + + if not(os.path.exists(datadir)) or not(os.path.exists(repodir)): + status = 400 + + elif cmd == 'source': path = arg if len(path) > 0 and path[-1] == '/': path = path[:-1] @@ -64,6 +74,7 @@ def print(arg, end='\n'): if not search('^[A-Za-z0-9_/.,+-]*$', path): path = 'INVALID' url = 'source'+path + elif cmd == 'ident': ident = arg[1:] form = cgi.FieldStorage() @@ -82,13 +93,6 @@ def print(arg, end='\n'): else: status = 400 -basedir = os.environ['LXR_PROJ_DIR'] -datadir = basedir + '/' + project + '/data'; -repodir = basedir + '/' + project + '/repo'; - -if not(os.path.exists(datadir)) or not(os.path.exists(repodir)): - status = 400 - if status == 301: realprint('Status: 301 Moved Permanently') realprint('Location: '+location+'\n') From de38f2625bc8bd1093e290a70f770c27ff7c152f Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 20 Dec 2019 10:07:08 +0100 Subject: [PATCH 007/529] Document that the command line interface is currently broken Signed-off-by: Michael Opdenacker --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c6f5464b..a1d9e6c8 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ cd /usr/local/elixir/ ### Second Test +**Caution: the below test, based on the command line interface, is currently broken. Support for the command line interface should be restored by an upcoming commit.** + Verify that the queries work: ``` From e969b88e8b1ea74297d12b3667e3efd0fe6f76f7 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Mon, 30 Dec 2019 15:00:44 -0500 Subject: [PATCH 008/529] Implemented query cmd commands for ident and file --- README.md | 6 ++---- query.py | 50 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a1d9e6c8..163e9622 100644 --- a/README.md +++ b/README.md @@ -121,13 +121,11 @@ cd /usr/local/elixir/ ### Second Test -**Caution: the below test, based on the command line interface, is currently broken. Support for the command line interface should be restored by an upcoming commit.** - Verify that the queries work: ``` -$ ./query.py file v4.10 /kernel/sched/clock.c -$ ./query.py ident v4.10 raw_spin_unlock_irq +$ ./query.py v4.10 ident raw_spin_unlock_irq +$ ./query.py v4.10 file /kernel/sched/clock.c ``` Note: `v4.10` can be replaced with any other tag. diff --git a/query.py b/query.py index 9f261618..4fc07de0 100755 --- a/query.py +++ b/query.py @@ -34,6 +34,16 @@ def __init__(self, path, line, type=None): self.line = line self.type = type + def __repr__(self): + type_repr = "" + if self.type: + type_repr = f" , type: {self.type}" + + return f"Symbol in path: {self.path}, line: {self.line}" + type_repr + + def __str__(self): + return self.__repr__() + def decode(byte_object): # decode('ascii') fails on special chars # FIXME: major hack until we handle everything as bytestrings @@ -95,8 +105,8 @@ def query(cmd, *args): elif cmd == 'dir': - # Returns the contents (trees or blobs) of the specified directory - # Example: ./query.py dir v3.1-rc10 /arch + # Returns the contents (trees or blobs) of the specified directory + # Example: ./query.py dir v3.1-rc10 /arch version = args[0] path = args[1] @@ -105,7 +115,7 @@ def query(cmd, *args): elif cmd == 'file': - # Returns the contents of the specified file + # Returns the contents of the specified file # Tokens are marked for further processing # Example: ./query.py file v3.1-rc10 /Makefile @@ -129,7 +139,7 @@ def query(cmd, *args): elif cmd == 'ident': - # Returns identifier search results + # Returns identifier search results version = args[0] ident = args[1] @@ -179,8 +189,34 @@ def query(cmd, *args): else: return('Unknown subcommand: ' + cmd + '\n') +def cmd_ident(version, ident, **kwargs): + symbol_definitions, symbol_references = query("ident", version, ident) + print("Symbol Definitions:") + for symbol_definition in symbol_definitions: + print(symbol_definition) + + print("\nSymbol References:") + for symbol_reference in symbol_references: + print(symbol_reference) + +def cmd_file(version, path, **kwargs): + code = query("file", version, path) + print(code) + if __name__ == "__main__": - import sys + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("version", help="The version of the project", type=str, default="latest") + subparsers = parser.add_subparsers() + + ident_subparser = subparsers.add_parser('ident', help="Get definitions and references of an identifier") + ident_subparser.add_argument('ident', type=str, help="The name of the identifier") + ident_subparser.set_defaults(func=cmd_ident) + + file_subparser = subparsers.add_parser('file', help="Get a source file") + file_subparser.add_argument('path', type=str, help="The path of the source file") + file_subparser.set_defaults(func=cmd_file) - output = query(*(sys.argv[1:])) - sys.stdout.buffer.write(output) + args = parser.parse_args() + args.func(**vars(args)) From 777362c78755928ea23fb3025a23b26d3282b17c Mon Sep 17 00:00:00 2001 From: Christopher White Date: Fri, 3 Jan 2020 12:44:58 -0500 Subject: [PATCH 009/529] README: include URL for Linux sublevel tags The GitHub repo doesn't have tags for sublevel (X.Y.Z) releases, unlike the master Bootlin Elixir site. Add the master repo that does have the sublevel tags to the README. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 163e9622..b378a215 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,13 @@ And then run `source /etc/profile`. ### Clone Kernel source code -``` -git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git /path/elixir-data/linux/repo/ -``` +Run one of the following, depending on whether or not you want `SUBLEVEL` +(X.Y.Z) tags: + +* `git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git /path/elixir-data/linux/repo/` + * Has only X.Y and X.Y-rcZ tags +* `git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git /path/elixir-data/linux/repo/` + * Has X.Y.Z tags ### First Test @@ -382,7 +386,7 @@ The response body is of the following structure: { "definitions": [{"path": "commands/loadb.c", "line": 71, "type": "variable"}, ...], - "references": + "references": [{"path": "arch/arm/boards/cm-fx6/board.c", "line": "64,64,71,72,75", "type": null}, ...] } ``` From 4b9e9e9e27ade3d8ceb0b52387d18e652d78f544 Mon Sep 17 00:00:00 2001 From: Jules Maselbas Date: Tue, 15 Oct 2019 12:03:52 +0200 Subject: [PATCH 010/529] Support for lighthttpd CGI Sample config for lighthttpd rules: server.document-root = server_root + "/elixir/http" url.redirect = ( "^/$" => "/linux/latest/source" ) url.rewrite = ( "^/.*/(source|ident|search)" => "/web.py/$1") setenv.add-environment = ( "PYTHONIOENCODING" => "utf-8", "LXR_PROJ_DIR" => "/path/to/elixir-data" ) --- http/web.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http/web.py b/http/web.py index f6900f8e..4fee7713 100755 --- a/http/web.py +++ b/http/web.py @@ -40,8 +40,9 @@ def print(arg, end='\n'): ident = '' status = 200 +url = os.environ.get('REQUEST_URI') or os.environ.get('SCRIPT_URL') # Split the URL into its components (project, version, cmd, arg) -m = search('^/([^/]*)/([^/]*)/([^/]*)(.*)$', os.environ['SCRIPT_URL']) +m = search('^/([^/]*)/([^/]*)/([^/]*)(.*)$', url) if m: project = m.group(1) From cb495269ebca404ebf8df8bd1f972e0bb35880f8 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 29 Jan 2020 08:11:27 +0100 Subject: [PATCH 011/529] Remove blacklisted structure names - Those were important structures in Linux They deserve to be indexed. - This will require re-indexing, so this will take a bit of time to be fixed in the database Signed-off-by: Michael Opdenacker --- lib.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib.py b/lib.py index 07745cc0..a9a521af 100755 --- a/lib.py +++ b/lib.py @@ -126,7 +126,6 @@ def unescape(bstr): b'next', b'ctx', b'event', - b'mddev', b'q', b'attr', b'cpu', @@ -144,12 +143,10 @@ def unescape(bstr): b'NULL', b'sizeof', b'status', - b'device', b'adapter', b'inline', b'offset', b'failed', - b'dentry', b'retval', b'buffer', b'length', From 3037fc624eec77d4c34ec805e2cf79e5f7939254 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 29 Jan 2020 11:18:07 +0100 Subject: [PATCH 012/529] Improve details about fetching the Linux "stable" tree Signed-off-by: Michael Opdenacker --- README.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b378a215..9db023b6 100644 --- a/README.md +++ b/README.md @@ -100,13 +100,22 @@ And then run `source /etc/profile`. ### Clone Kernel source code -Run one of the following, depending on whether or not you want `SUBLEVEL` -(X.Y.Z) tags: +First clone the master tree released by Linus Torvalds: -* `git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git /path/elixir-data/linux/repo/` - * Has only X.Y and X.Y-rcZ tags -* `git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git /path/elixir-data/linux/repo/` - * Has X.Y.Z tags +``` +cd /pathy/elixir-data/linux +git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git repo +``` + +Then, you should also declare a `stable` remote branch corresponding to the `stable` tree, to get all release updates: + +``` +cd repo +git remote add stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git +git fetch stable +``` + +Feel free to add more remote branches in this way, as Elixir will consider tags from all remote branches. ### First Test @@ -303,6 +312,10 @@ repository for the project you want to support: cd /srv/git git clone --bare https://github.com/zephyrproject-rtos/zephyr +After doing this, you may also reference and fetch remote branches for this project, +for example corresponding to the `stable` tree for the Linux kernel (see the +instructions for Linux earlier in this document). + Now, in your `LXR_PROJ_DIR` directory, create a new directory for the new project: From 7eae1017604a50e173ea0d96dc4268f9c03eed7d Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 30 Jan 2020 14:22:32 +0100 Subject: [PATCH 013/529] README.md: add sample configuration for lighthttpd - Thanks to 'jmaselbas' See https://github.com/bootlin/elixir/commit/bbd8cc3bf24751a96801d886b0bfc61522ebb7b1 Signed-off-by: Michael Opdenacker --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 9db023b6..da7e567e 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,17 @@ Finally, start the httpd server. systemctl start httpd ``` +### Configure lighthttpd + +Here's a sample configuration for lighthttpd: +``` +server.document-root = server_root + "/elixir/http" +url.redirect = ( "^/$" => "/linux/latest/source" ) +url.rewrite = ( "^/.*/(source|ident|search)" => "/web.py/$1") +setenv.add-environment = ( "PYTHONIOENCODING" => "utf-8", + "LXR_PROJ_DIR" => "/path/to/elixir-data" ) +``` + ### Using a cache to improve performance At Bootlin, we're using the [Varnish http cache](https://varnish-cache.org/) From 3de325ca869cedecb3ed62bf9451c4c5a98f6954 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 4 Feb 2020 10:51:24 +0100 Subject: [PATCH 014/529] Improve and update website banner Signed-off-by: Michael Opdenacker --- http/banner.css | 26 ++++++++------------------ templates/header.html | 14 ++++++++------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/http/banner.css b/http/banner.css index ec9507d9..4eba234f 100644 --- a/http/banner.css +++ b/http/banner.css @@ -1,16 +1,16 @@ .message-banner-desktop { position: absolute; - padding: 10px; + padding: 5px; -webkit-box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); -moz-box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); - font-size: 30px; + font-size: 14; font-family: Arial; font-weight: bold; left: 10px; bottom: 30px; - width: 150px; - height: 120px; + width: 215px; + height: 100px; text-align: center; border-radius: 20px; background: rgba(255,255,255,1); @@ -23,27 +23,17 @@ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed', GradientType=0 ); } .message-banner-desktop p.title { - font-size: 30px; + font-size: 14px; margin: 0; margin-top: 5px; + margin-bottom: 8px; } .message-banner-desktop .subtitle { - width: 150px; + width: 210px; padding: 0 10px; - background: #fe003e; - -webkit-box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); - -moz-box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); - box-shadow: -2px 0px 15px 1px rgba(0,0,0,0.69); - -webkit-transform: rotate(-15deg); - -moz-transform: rotate(-15deg); - -o-transform: rotate(-15deg); - -ms-transform: rotate(-15deg); - transform: rotate(-15deg); - font-size: 15px; + font-size: 11px; position: relative; - top: -10px; z-index: 29; - color: #fff; } .message-banner-mobile { diff --git a/templates/header.html b/templates/header.html index ecba62e6..2248f4ad 100644 --- a/templates/header.html +++ b/templates/header.html @@ -1,13 +1,15 @@
-

New training

-

Linux graphics course

- +

Training sessions

+

Kernel and Embedded Linux

+
-

New training

-

Linux graphics course

- +

Next training sessions

+
Linux Kernel: March 16-20
+
Embedded Linux: May 11-15
+
and on-site sessions
+

From b22a562b5bbc3cefee3892b5f829481fd78cee95 Mon Sep 17 00:00:00 2001 From: Christopher White Date: Tue, 21 Jan 2020 09:27:46 -0500 Subject: [PATCH 015/529] Added files copied from Linux v5.4 These files all carry SPDX GPL2+ indicators. Based on the information available to me, I have a good-faith belief that they can be combined into this AGPLv3 project and licensed AGPLv3. This commit only touches t/tree. Therefore, if the test suite later needs to be changed, it will be sufficient to revert this commit. The files used are those involved in the x64 compilation of drivers/i2c/i2c-core-base.c, filtered to remove any that aren't tagged GPL2+. Note that t/tree itself is not a Git repo or submodule. It is a collection of files that test scripts can use to populate a Git repo. --- t/tree/arch/x86/include/asm/acpi.h | 187 ++ t/tree/arch/x86/include/asm/ist.h | 14 + t/tree/arch/x86/include/asm/orc_types.h | 97 + t/tree/arch/x86/include/asm/uprobes.h | 58 + t/tree/arch/x86/include/uapi/asm/ist.h | 30 + t/tree/drivers/i2c/i2c-boardinfo.c | 104 + t/tree/drivers/i2c/i2c-core-acpi.c | 760 ++++++ t/tree/drivers/i2c/i2c-core-base.c | 2399 ++++++++++++++++++ t/tree/drivers/i2c/i2c-core-of.c | 281 ++ t/tree/drivers/i2c/i2c-core-slave.c | 111 + t/tree/drivers/i2c/i2c-core-smbus.c | 720 ++++++ t/tree/drivers/i2c/i2c-core.h | 97 + t/tree/drivers/i2c/i2c-dev.c | 766 ++++++ t/tree/drivers/i2c/i2c-smbus.c | 208 ++ t/tree/drivers/i2c/i2c-stub.c | 414 +++ t/tree/include/acpi/acpi_bus.h | 697 +++++ t/tree/include/acpi/acpi_drivers.h | 118 + t/tree/include/asm-generic/barrier.h | 261 ++ t/tree/include/asm-generic/io.h | 1124 ++++++++ t/tree/include/asm-generic/pci_iomap.h | 55 + t/tree/include/asm-generic/qrwlock.h | 130 + t/tree/include/asm-generic/qspinlock.h | 116 + t/tree/include/asm-generic/qspinlock_types.h | 103 + t/tree/include/linux/acpi.h | 1302 ++++++++++ t/tree/include/linux/apm_bios.h | 92 + t/tree/include/linux/assoc_array.h | 88 + t/tree/include/linux/cred.h | 425 ++++ t/tree/include/linux/i2c-smbus.h | 47 + t/tree/include/linux/i2c.h | 1008 ++++++++ t/tree/include/linux/key.h | 500 ++++ t/tree/include/linux/kmod.h | 35 + t/tree/include/linux/log2.h | 257 ++ t/tree/include/linux/logic_pio.h | 124 + t/tree/include/linux/of.h | 1480 +++++++++++ t/tree/include/linux/of_platform.h | 116 + t/tree/include/linux/plist.h | 298 +++ t/tree/include/linux/pm.h | 813 ++++++ t/tree/include/linux/pm_wakeup.h | 201 ++ t/tree/include/linux/radix-tree.h | 460 ++++ t/tree/include/linux/rbtree.h | 161 ++ t/tree/include/linux/rcu_node_tree.h | 90 + t/tree/include/linux/rcu_segcblist.h | 90 + t/tree/include/linux/rcu_sync.h | 54 + t/tree/include/linux/rcupdate.h | 897 +++++++ t/tree/include/linux/rcutree.h | 68 + t/tree/include/linux/srcu.h | 205 ++ t/tree/include/linux/srcutree.h | 141 + t/tree/include/linux/stackdepot.h | 22 + t/tree/include/linux/uprobes.h | 204 ++ t/tree/include/linux/xarray.h | 1750 +++++++++++++ t/tree/include/trace/events/i2c.h | 146 ++ t/tree/include/uapi/linux/apm_bios.h | 138 + t/tree/include/uapi/linux/eventpoll.h | 94 + t/tree/include/uapi/linux/i2c.h | 158 ++ t/tree/include/uapi/linux/rseq.h | 147 ++ 55 files changed, 20461 insertions(+) create mode 100644 t/tree/arch/x86/include/asm/acpi.h create mode 100644 t/tree/arch/x86/include/asm/ist.h create mode 100644 t/tree/arch/x86/include/asm/orc_types.h create mode 100644 t/tree/arch/x86/include/asm/uprobes.h create mode 100644 t/tree/arch/x86/include/uapi/asm/ist.h create mode 100644 t/tree/drivers/i2c/i2c-boardinfo.c create mode 100644 t/tree/drivers/i2c/i2c-core-acpi.c create mode 100644 t/tree/drivers/i2c/i2c-core-base.c create mode 100644 t/tree/drivers/i2c/i2c-core-of.c create mode 100644 t/tree/drivers/i2c/i2c-core-slave.c create mode 100644 t/tree/drivers/i2c/i2c-core-smbus.c create mode 100644 t/tree/drivers/i2c/i2c-core.h create mode 100644 t/tree/drivers/i2c/i2c-dev.c create mode 100644 t/tree/drivers/i2c/i2c-smbus.c create mode 100644 t/tree/drivers/i2c/i2c-stub.c create mode 100644 t/tree/include/acpi/acpi_bus.h create mode 100644 t/tree/include/acpi/acpi_drivers.h create mode 100644 t/tree/include/asm-generic/barrier.h create mode 100644 t/tree/include/asm-generic/io.h create mode 100644 t/tree/include/asm-generic/pci_iomap.h create mode 100644 t/tree/include/asm-generic/qrwlock.h create mode 100644 t/tree/include/asm-generic/qspinlock.h create mode 100644 t/tree/include/asm-generic/qspinlock_types.h create mode 100644 t/tree/include/linux/acpi.h create mode 100644 t/tree/include/linux/apm_bios.h create mode 100644 t/tree/include/linux/assoc_array.h create mode 100644 t/tree/include/linux/cred.h create mode 100644 t/tree/include/linux/i2c-smbus.h create mode 100644 t/tree/include/linux/i2c.h create mode 100644 t/tree/include/linux/key.h create mode 100644 t/tree/include/linux/kmod.h create mode 100644 t/tree/include/linux/log2.h create mode 100644 t/tree/include/linux/logic_pio.h create mode 100644 t/tree/include/linux/of.h create mode 100644 t/tree/include/linux/of_platform.h create mode 100644 t/tree/include/linux/plist.h create mode 100644 t/tree/include/linux/pm.h create mode 100644 t/tree/include/linux/pm_wakeup.h create mode 100644 t/tree/include/linux/radix-tree.h create mode 100644 t/tree/include/linux/rbtree.h create mode 100644 t/tree/include/linux/rcu_node_tree.h create mode 100644 t/tree/include/linux/rcu_segcblist.h create mode 100644 t/tree/include/linux/rcu_sync.h create mode 100644 t/tree/include/linux/rcupdate.h create mode 100644 t/tree/include/linux/rcutree.h create mode 100644 t/tree/include/linux/srcu.h create mode 100644 t/tree/include/linux/srcutree.h create mode 100644 t/tree/include/linux/stackdepot.h create mode 100644 t/tree/include/linux/uprobes.h create mode 100644 t/tree/include/linux/xarray.h create mode 100644 t/tree/include/trace/events/i2c.h create mode 100644 t/tree/include/uapi/linux/apm_bios.h create mode 100644 t/tree/include/uapi/linux/eventpoll.h create mode 100644 t/tree/include/uapi/linux/i2c.h create mode 100644 t/tree/include/uapi/linux/rseq.h diff --git a/t/tree/arch/x86/include/asm/acpi.h b/t/tree/arch/x86/include/asm/acpi.h new file mode 100644 index 00000000..bc9693c9 --- /dev/null +++ b/t/tree/arch/x86/include/asm/acpi.h @@ -0,0 +1,187 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ASM_X86_ACPI_H +#define _ASM_X86_ACPI_H + +/* + * Copyright (C) 2001 Paul Diefenbaugh + * Copyright (C) 2001 Patrick Mochel + */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ACPI_APEI +# include +#endif + +#ifdef CONFIG_ACPI +extern int acpi_lapic; +extern int acpi_ioapic; +extern int acpi_noirq; +extern int acpi_strict; +extern int acpi_disabled; +extern int acpi_pci_disabled; +extern int acpi_skip_timer_override; +extern int acpi_use_timer_override; +extern int acpi_fix_pin2_polarity; +extern int acpi_disable_cmcff; + +extern u8 acpi_sci_flags; +extern u32 acpi_sci_override_gsi; +void acpi_pic_sci_set_trigger(unsigned int, u16); + +struct device; + +extern int (*__acpi_register_gsi)(struct device *dev, u32 gsi, + int trigger, int polarity); +extern void (*__acpi_unregister_gsi)(u32 gsi); + +static inline void disable_acpi(void) +{ + acpi_disabled = 1; + acpi_pci_disabled = 1; + acpi_noirq = 1; +} + +extern int acpi_gsi_to_irq(u32 gsi, unsigned int *irq); + +static inline void acpi_noirq_set(void) { acpi_noirq = 1; } +static inline void acpi_disable_pci(void) +{ + acpi_pci_disabled = 1; + acpi_noirq_set(); +} + +/* Low-level suspend routine. */ +extern int (*acpi_suspend_lowlevel)(void); + +/* Physical address to resume after wakeup */ +#define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start)) + +/* + * Check if the CPU can handle C2 and deeper + */ +static inline unsigned int acpi_processor_cstate_check(unsigned int max_cstate) +{ + /* + * Early models (<=5) of AMD Opterons are not supposed to go into + * C2 state. + * + * Steppings 0x0A and later are good + */ + if (boot_cpu_data.x86 == 0x0F && + boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86_model <= 0x05 && + boot_cpu_data.x86_stepping < 0x0A) + return 1; + else if (boot_cpu_has(X86_BUG_AMD_APIC_C1E)) + return 1; + else + return max_cstate; +} + +static inline bool arch_has_acpi_pdc(void) +{ + struct cpuinfo_x86 *c = &cpu_data(0); + return (c->x86_vendor == X86_VENDOR_INTEL || + c->x86_vendor == X86_VENDOR_CENTAUR); +} + +static inline void arch_acpi_set_pdc_bits(u32 *buf) +{ + struct cpuinfo_x86 *c = &cpu_data(0); + + buf[2] |= ACPI_PDC_C_CAPABILITY_SMP; + + if (cpu_has(c, X86_FEATURE_EST)) + buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP; + + if (cpu_has(c, X86_FEATURE_ACPI)) + buf[2] |= ACPI_PDC_T_FFH; + + /* + * If mwait/monitor is unsupported, C2/C3_FFH will be disabled + */ + if (!cpu_has(c, X86_FEATURE_MWAIT)) + buf[2] &= ~(ACPI_PDC_C_C2C3_FFH); +} + +static inline bool acpi_has_cpu_in_madt(void) +{ + return !!acpi_lapic; +} + +#define ACPI_HAVE_ARCH_SET_ROOT_POINTER +static inline void acpi_arch_set_root_pointer(u64 addr) +{ + x86_init.acpi.set_root_pointer(addr); +} + +#define ACPI_HAVE_ARCH_GET_ROOT_POINTER +static inline u64 acpi_arch_get_root_pointer(void) +{ + return x86_init.acpi.get_root_pointer(); +} + +void acpi_generic_reduced_hw_init(void); + +void x86_default_set_root_pointer(u64 addr); +u64 x86_default_get_root_pointer(void); + +#else /* !CONFIG_ACPI */ + +#define acpi_lapic 0 +#define acpi_ioapic 0 +#define acpi_disable_cmcff 0 +static inline void acpi_noirq_set(void) { } +static inline void acpi_disable_pci(void) { } +static inline void disable_acpi(void) { } + +static inline void acpi_generic_reduced_hw_init(void) { } + +static inline void x86_default_set_root_pointer(u64 addr) { } + +static inline u64 x86_default_get_root_pointer(void) +{ + return 0; +} + +#endif /* !CONFIG_ACPI */ + +#define ARCH_HAS_POWER_INIT 1 + +#ifdef CONFIG_ACPI_NUMA +extern int x86_acpi_numa_init(void); +#endif /* CONFIG_ACPI_NUMA */ + +#define acpi_unlazy_tlb(x) leave_mm(x) + +#ifdef CONFIG_ACPI_APEI +static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr) +{ + /* + * We currently have no way to look up the EFI memory map + * attributes for a region in a consistent way, because the + * memmap is discarded after efi_free_boot_services(). So if + * you call efi_mem_attributes() during boot and at runtime, + * you could theoretically see different attributes. + * + * We are yet to see any x86 platforms that require anything + * other than PAGE_KERNEL (some ARM64 platforms require the + * equivalent of PAGE_KERNEL_NOCACHE). Additionally, if SME + * is active, the ACPI information will not be encrypted, + * so return PAGE_KERNEL_NOENC until we know differently. + */ + return PAGE_KERNEL_NOENC; +} +#endif + +#define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn_mapped << PAGE_SHIFT) + +#endif /* _ASM_X86_ACPI_H */ diff --git a/t/tree/arch/x86/include/asm/ist.h b/t/tree/arch/x86/include/asm/ist.h new file mode 100644 index 00000000..7ede2731 --- /dev/null +++ b/t/tree/arch/x86/include/asm/ist.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Include file for the interface to IST BIOS + * Copyright 2002 Andy Grover + */ +#ifndef _ASM_X86_IST_H +#define _ASM_X86_IST_H + +#include + + +extern struct ist_info ist_info; + +#endif /* _ASM_X86_IST_H */ diff --git a/t/tree/arch/x86/include/asm/orc_types.h b/t/tree/arch/x86/include/asm/orc_types.h new file mode 100644 index 00000000..6e060907 --- /dev/null +++ b/t/tree/arch/x86/include/asm/orc_types.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017 Josh Poimboeuf + */ + +#ifndef _ORC_TYPES_H +#define _ORC_TYPES_H + +#include +#include + +/* + * The ORC_REG_* registers are base registers which are used to find other + * registers on the stack. + * + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the + * address of the previous frame: the caller's SP before it called the current + * function. + * + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in + * the current frame. + * + * The most commonly used base registers are SP and BP -- which the previous SP + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is + * usually based on. + * + * The rest of the base registers are needed for special cases like entry code + * and GCC realigned stacks. + */ +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_DX 2 +#define ORC_REG_DI 3 +#define ORC_REG_BP 4 +#define ORC_REG_SP 5 +#define ORC_REG_R10 6 +#define ORC_REG_R13 7 +#define ORC_REG_BP_INDIRECT 8 +#define ORC_REG_SP_INDIRECT 9 +#define ORC_REG_MAX 15 + +/* + * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the + * caller's SP right before it made the call). Used for all callable + * functions, i.e. all C code and all callable asm functions. + * + * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points + * to a fully populated pt_regs from a syscall, interrupt, or exception. + * + * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset + * points to the iret return frame. + * + * The UNWIND_HINT macros are used only for the unwind_hint struct. They + * aren't used in struct orc_entry due to size and complexity constraints. + * Objtool converts them to real types when it converts the hints to orc + * entries. + */ +#define ORC_TYPE_CALL 0 +#define ORC_TYPE_REGS 1 +#define ORC_TYPE_REGS_IRET 2 +#define UNWIND_HINT_TYPE_SAVE 3 +#define UNWIND_HINT_TYPE_RESTORE 4 + +#ifndef __ASSEMBLY__ +/* + * This struct is more or less a vastly simplified version of the DWARF Call + * Frame Information standard. It contains only the necessary parts of DWARF + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the + * unwinder how to find the previous SP and BP (and sometimes entry regs) on + * the stack for a given code address. Each instance of the struct corresponds + * to one or more code locations. + */ +struct orc_entry { + s16 sp_offset; + s16 bp_offset; + unsigned sp_reg:4; + unsigned bp_reg:4; + unsigned type:2; + unsigned end:1; +} __packed; + +/* + * This struct is used by asm and inline asm code to manually annotate the + * location of registers on the stack for the ORC unwinder. + * + * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*. + */ +struct unwind_hint { + u32 ip; + s16 sp_offset; + u8 sp_reg; + u8 type; + u8 end; +}; +#endif /* __ASSEMBLY__ */ + +#endif /* _ORC_TYPES_H */ diff --git a/t/tree/arch/x86/include/asm/uprobes.h b/t/tree/arch/x86/include/asm/uprobes.h new file mode 100644 index 00000000..678fb546 --- /dev/null +++ b/t/tree/arch/x86/include/asm/uprobes.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ASM_UPROBES_H +#define _ASM_UPROBES_H +/* + * User-space Probes (UProbes) for x86 + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +#include + +typedef u8 uprobe_opcode_t; + +#define MAX_UINSN_BYTES 16 +#define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ + +#define UPROBE_SWBP_INSN 0xcc +#define UPROBE_SWBP_INSN_SIZE 1 + +struct uprobe_xol_ops; + +struct arch_uprobe { + union { + u8 insn[MAX_UINSN_BYTES]; + u8 ixol[MAX_UINSN_BYTES]; + }; + + const struct uprobe_xol_ops *ops; + + union { + struct { + s32 offs; + u8 ilen; + u8 opc1; + } branch; + struct { + u8 fixups; + u8 ilen; + } defparam; + struct { + u8 reg_offset; /* to the start of pt_regs */ + u8 ilen; + } push; + }; +}; + +struct arch_uprobe_task { +#ifdef CONFIG_X86_64 + unsigned long saved_scratch_register; +#endif + unsigned int saved_trap_nr; + unsigned int saved_tf; +}; + +#endif /* _ASM_UPROBES_H */ diff --git a/t/tree/arch/x86/include/uapi/asm/ist.h b/t/tree/arch/x86/include/uapi/asm/ist.h new file mode 100644 index 00000000..eac5b207 --- /dev/null +++ b/t/tree/arch/x86/include/uapi/asm/ist.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Include file for the interface to IST BIOS + * Copyright 2002 Andy Grover + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef _UAPI_ASM_X86_IST_H +#define _UAPI_ASM_X86_IST_H + + + +#include + +struct ist_info { + __u32 signature; + __u32 command; + __u32 event; + __u32 perf_level; +}; + +#endif /* _UAPI_ASM_X86_IST_H */ diff --git a/t/tree/drivers/i2c/i2c-boardinfo.c b/t/tree/drivers/i2c/i2c-boardinfo.c new file mode 100644 index 00000000..8bc51d4e --- /dev/null +++ b/t/tree/drivers/i2c/i2c-boardinfo.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c-boardinfo.c - collect pre-declarations of I2C devices + */ + +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + + +/* These symbols are exported ONLY FOR the i2c core. + * No other users will be supported. + */ +DECLARE_RWSEM(__i2c_board_lock); +EXPORT_SYMBOL_GPL(__i2c_board_lock); + +LIST_HEAD(__i2c_board_list); +EXPORT_SYMBOL_GPL(__i2c_board_list); + +int __i2c_first_dynamic_bus_num; +EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num); + + +/** + * i2c_register_board_info - statically declare I2C devices + * @busnum: identifies the bus to which these devices belong + * @info: vector of i2c device descriptors + * @len: how many descriptors in the vector; may be zero to reserve + * the specified bus number. + * + * Systems using the Linux I2C driver stack can declare tables of board info + * while they initialize. This should be done in board-specific init code + * near arch_initcall() time, or equivalent, before any I2C adapter driver is + * registered. For example, mainboard init code could define several devices, + * as could the init code for each daughtercard in a board stack. + * + * The I2C devices will be created later, after the adapter for the relevant + * bus has been registered. After that moment, standard driver model tools + * are used to bind "new style" I2C drivers to the devices. The bus number + * for any device declared using this routine is not available for dynamic + * allocation. + * + * The board info passed can safely be __initdata, but be careful of embedded + * pointers (for platform_data, functions, etc) since that won't be copied. + * Device properties are deep-copied though. + */ +int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) +{ + int status; + + down_write(&__i2c_board_lock); + + /* dynamic bus numbers will be assigned after the last static one */ + if (busnum >= __i2c_first_dynamic_bus_num) + __i2c_first_dynamic_bus_num = busnum + 1; + + for (status = 0; len; len--, info++) { + struct i2c_devinfo *devinfo; + + devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); + if (!devinfo) { + pr_debug("i2c-core: can't register boardinfo!\n"); + status = -ENOMEM; + break; + } + + devinfo->busnum = busnum; + devinfo->board_info = *info; + + if (info->properties) { + devinfo->board_info.properties = + property_entries_dup(info->properties); + if (IS_ERR(devinfo->board_info.properties)) { + status = PTR_ERR(devinfo->board_info.properties); + kfree(devinfo); + break; + } + } + + if (info->resources) { + devinfo->board_info.resources = + kmemdup(info->resources, + info->num_resources * + sizeof(*info->resources), + GFP_KERNEL); + if (!devinfo->board_info.resources) { + status = -ENOMEM; + kfree(devinfo); + break; + } + } + + list_add_tail(&devinfo->list, &__i2c_board_list); + } + + up_write(&__i2c_board_lock); + + return status; +} diff --git a/t/tree/drivers/i2c/i2c-core-acpi.c b/t/tree/drivers/i2c/i2c-core-acpi.c new file mode 100644 index 00000000..62a1c92a --- /dev/null +++ b/t/tree/drivers/i2c/i2c-core-acpi.c @@ -0,0 +1,760 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core ACPI support code + * + * Copyright (C) 2014 Intel Corp, Author: Lan Tianyu + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +struct i2c_acpi_handler_data { + struct acpi_connection_info info; + struct i2c_adapter *adapter; +}; + +struct gsb_buffer { + u8 status; + u8 len; + union { + u16 wdata; + u8 bdata; + u8 data[0]; + }; +} __packed; + +struct i2c_acpi_lookup { + struct i2c_board_info *info; + acpi_handle adapter_handle; + acpi_handle device_handle; + acpi_handle search_handle; + int n; + int index; + u32 speed; + u32 min_speed; + u32 force_speed; +}; + +/** + * i2c_acpi_get_i2c_resource - Gets I2cSerialBus resource if type matches + * @ares: ACPI resource + * @i2c: Pointer to I2cSerialBus resource will be returned here + * + * Checks if the given ACPI resource is of type I2cSerialBus. + * In this case, returns a pointer to it to the caller. + * + * Returns true if resource type is of I2cSerialBus, otherwise false. + */ +bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c) +{ + struct acpi_resource_i2c_serialbus *sb; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return false; + + sb = &ares->data.i2c_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) + return false; + + *i2c = sb; + return true; +} +EXPORT_SYMBOL_GPL(i2c_acpi_get_i2c_resource); + +static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) +{ + struct i2c_acpi_lookup *lookup = data; + struct i2c_board_info *info = lookup->info; + struct acpi_resource_i2c_serialbus *sb; + acpi_status status; + + if (info->addr || !i2c_acpi_get_i2c_resource(ares, &sb)) + return 1; + + if (lookup->index != -1 && lookup->n++ != lookup->index) + return 1; + + status = acpi_get_handle(lookup->device_handle, + sb->resource_source.string_ptr, + &lookup->adapter_handle); + if (ACPI_FAILURE(status)) + return 1; + + info->addr = sb->slave_address; + lookup->speed = sb->connection_speed; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; + + return 1; +} + +static const struct acpi_device_id i2c_acpi_ignored_device_ids[] = { + /* + * ACPI video acpi_devices, which are handled by the acpi-video driver + * sometimes contain a SERIAL_TYPE_I2C ACPI resource, ignore these. + */ + { ACPI_VIDEO_HID, 0 }, + {} +}; + +static int i2c_acpi_do_lookup(struct acpi_device *adev, + struct i2c_acpi_lookup *lookup) +{ + struct i2c_board_info *info = lookup->info; + struct list_head resource_list; + int ret; + + if (acpi_bus_get_status(adev) || !adev->status.present) + return -EINVAL; + + if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) + return -ENODEV; + + memset(info, 0, sizeof(*info)); + lookup->device_handle = acpi_device_handle(adev); + + /* Look up for I2cSerialBus resource */ + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_fill_info, lookup); + acpi_dev_free_resource_list(&resource_list); + + if (ret < 0 || !info->addr) + return -EINVAL; + + return 0; +} + +static int i2c_acpi_add_resource(struct acpi_resource *ares, void *data) +{ + int *irq = data; + struct resource r; + + if (*irq <= 0 && acpi_dev_resource_interrupt(ares, 0, &r)) + *irq = i2c_dev_irq_from_resources(&r, 1); + + return 1; /* No need to add resource to the list */ +} + +/** + * i2c_acpi_get_irq - get device IRQ number from ACPI + * @client: Pointer to the I2C client device + * + * Find the IRQ number used by a specific client device. + * + * Return: The IRQ number or an error code. + */ +int i2c_acpi_get_irq(struct i2c_client *client) +{ + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + struct list_head resource_list; + int irq = -ENOENT; + int ret; + + INIT_LIST_HEAD(&resource_list); + + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_add_resource, &irq); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resource_list); + + if (irq == -ENOENT) + irq = acpi_dev_gpio_irq_get(adev, 0); + + return irq; +} + +static int i2c_acpi_get_info(struct acpi_device *adev, + struct i2c_board_info *info, + struct i2c_adapter *adapter, + acpi_handle *adapter_handle) +{ + struct i2c_acpi_lookup lookup; + int ret; + + memset(&lookup, 0, sizeof(lookup)); + lookup.info = info; + lookup.index = -1; + + if (acpi_device_enumerated(adev)) + return -EINVAL; + + ret = i2c_acpi_do_lookup(adev, &lookup); + if (ret) + return ret; + + if (adapter) { + /* The adapter must match the one in I2cSerialBus() connector */ + if (ACPI_HANDLE(&adapter->dev) != lookup.adapter_handle) + return -ENODEV; + } else { + struct acpi_device *adapter_adev; + + /* The adapter must be present */ + if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev)) + return -ENODEV; + if (acpi_bus_get_status(adapter_adev) || + !adapter_adev->status.present) + return -ENODEV; + } + + info->fwnode = acpi_fwnode_handle(adev); + if (adapter_handle) + *adapter_handle = lookup.adapter_handle; + + acpi_set_modalias(adev, dev_name(&adev->dev), info->type, + sizeof(info->type)); + + return 0; +} + +static void i2c_acpi_register_device(struct i2c_adapter *adapter, + struct acpi_device *adev, + struct i2c_board_info *info) +{ + adev->power.flags.ignore_parent = true; + acpi_device_set_enumerated(adev); + + if (!i2c_new_device(adapter, info)) { + adev->power.flags.ignore_parent = false; + dev_err(&adapter->dev, + "failed to add I2C device %s from ACPI\n", + dev_name(&adev->dev)); + } +} + +static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_adapter *adapter = data; + struct acpi_device *adev; + struct i2c_board_info info; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (i2c_acpi_get_info(adev, &info, adapter, NULL)) + return AE_OK; + + i2c_acpi_register_device(adapter, adev, &info); + + return AE_OK; +} + +#define I2C_ACPI_MAX_SCAN_DEPTH 32 + +/** + * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter + * @adap: pointer to adapter + * + * Enumerate all I2C slave devices behind this adapter by walking the ACPI + * namespace. When a device is found it will be added to the Linux device + * model and bound to the corresponding ACPI handle. + */ +void i2c_acpi_register_devices(struct i2c_adapter *adap) +{ + acpi_status status; + + if (!has_acpi_companion(&adap->dev)) + return; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_add_device, NULL, + adap, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); +} + +const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client) +{ + if (!(client && matches)) + return NULL; + + return acpi_match_device(matches, &client->dev); +} + +static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { + /* + * These Silead touchscreen controllers only work at 400KHz, for + * some reason they do not work at 100KHz. On some devices the ACPI + * tables list another device at their bus as only being capable + * of 100KHz, testing has shown that these other devices work fine + * at 400KHz (as can be expected of any recent i2c hw) so we force + * the speed of the bus to 400 KHz if a Silead device is present. + */ + { "MSSL1680", 0 }, + {} +}; + +static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_acpi_lookup *lookup = data; + struct acpi_device *adev; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (i2c_acpi_do_lookup(adev, lookup)) + return AE_OK; + + if (lookup->search_handle != lookup->adapter_handle) + return AE_OK; + + if (lookup->speed <= lookup->min_speed) + lookup->min_speed = lookup->speed; + + if (acpi_match_device_ids(adev, i2c_acpi_force_400khz_device_ids) == 0) + lookup->force_speed = 400000; + + return AE_OK; +} + +/** + * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI + * @dev: The device owning the bus + * + * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves + * devices connected to this bus and use the speed of slowest device. + * + * Returns the speed in Hz or zero + */ +u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + struct i2c_acpi_lookup lookup; + struct i2c_board_info dummy; + acpi_status status; + + if (!has_acpi_companion(dev)) + return 0; + + memset(&lookup, 0, sizeof(lookup)); + lookup.search_handle = ACPI_HANDLE(dev); + lookup.min_speed = UINT_MAX; + lookup.info = &dummy; + lookup.index = -1; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_lookup_speed, NULL, + &lookup, NULL); + + if (ACPI_FAILURE(status)) { + dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); + return 0; + } + + if (lookup.force_speed) { + if (lookup.force_speed != lookup.min_speed) + dev_warn(dev, FW_BUG "DSDT uses known not-working I2C bus speed %d, forcing it to %d\n", + lookup.min_speed, lookup.force_speed); + return lookup.force_speed; + } else if (lookup.min_speed != UINT_MAX) { + return lookup.min_speed; + } else { + return 0; + } +} +EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); + +static int i2c_acpi_find_match_adapter(struct device *dev, const void *data) +{ + struct i2c_adapter *adapter = i2c_verify_adapter(dev); + + if (!adapter) + return 0; + + return ACPI_HANDLE(dev) == (acpi_handle)data; +} + +struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) +{ + struct device *dev; + + dev = bus_find_device(&i2c_bus_type, NULL, handle, + i2c_acpi_find_match_adapter); + + return dev ? i2c_verify_adapter(dev) : NULL; +} +EXPORT_SYMBOL_GPL(i2c_acpi_find_adapter_by_handle); + +static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) +{ + struct device *dev; + + dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); + return dev ? i2c_verify_client(dev) : NULL; +} + +static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, + void *arg) +{ + struct acpi_device *adev = arg; + struct i2c_board_info info; + acpi_handle adapter_handle; + struct i2c_adapter *adapter; + struct i2c_client *client; + + switch (value) { + case ACPI_RECONFIG_DEVICE_ADD: + if (i2c_acpi_get_info(adev, &info, NULL, &adapter_handle)) + break; + + adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); + if (!adapter) + break; + + i2c_acpi_register_device(adapter, adev, &info); + break; + case ACPI_RECONFIG_DEVICE_REMOVE: + if (!acpi_device_enumerated(adev)) + break; + + client = i2c_acpi_find_client_by_adev(adev); + if (!client) + break; + + i2c_unregister_device(client); + put_device(&client->dev); + break; + } + + return NOTIFY_OK; +} + +struct notifier_block i2c_acpi_notifier = { + .notifier_call = i2c_acpi_notify, +}; + +/** + * i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource + * @dev: Device owning the ACPI resources to get the client from + * @index: Index of ACPI resource to get + * @info: describes the I2C device; note this is modified (addr gets set) + * Context: can sleep + * + * By default the i2c subsys creates an i2c-client for the first I2cSerialBus + * resource of an acpi_device, but some acpi_devices have multiple I2cSerialBus + * resources, in that case this function can be used to create an i2c-client + * for other I2cSerialBus resources in the Current Resource Settings table. + * + * Also see i2c_new_device, which this function calls to create the i2c-client. + * + * Returns a pointer to the new i2c-client, or error pointer in case of failure. + * Specifically, -EPROBE_DEFER is returned if the adapter is not found. + */ +struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, + struct i2c_board_info *info) +{ + struct i2c_acpi_lookup lookup; + struct i2c_adapter *adapter; + struct i2c_client *client; + struct acpi_device *adev; + LIST_HEAD(resource_list); + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return ERR_PTR(-EINVAL); + + memset(&lookup, 0, sizeof(lookup)); + lookup.info = info; + lookup.device_handle = acpi_device_handle(adev); + lookup.index = index; + + ret = acpi_dev_get_resources(adev, &resource_list, + i2c_acpi_fill_info, &lookup); + if (ret < 0) + return ERR_PTR(ret); + + acpi_dev_free_resource_list(&resource_list); + + if (!info->addr) + return ERR_PTR(-EADDRNOTAVAIL); + + adapter = i2c_acpi_find_adapter_by_handle(lookup.adapter_handle); + if (!adapter) + return ERR_PTR(-EPROBE_DEFER); + + client = i2c_new_device(adapter, info); + if (!client) + return ERR_PTR(-ENODEV); + + return client; +} +EXPORT_SYMBOL_GPL(i2c_acpi_new_device); + +#ifdef CONFIG_ACPI_I2C_OPREGION +static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, + u8 cmd, u8 *data, u8 data_len) +{ + + struct i2c_msg msgs[2]; + int ret; + u8 *buffer; + + buffer = kzalloc(data_len, GFP_KERNEL); + if (!buffer) + return AE_NO_MEMORY; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = &cmd; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = data_len; + msgs[1].buf = buffer; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + /* Getting a NACK is unfortunately normal with some DSTDs */ + if (ret == -EREMOTEIO) + dev_dbg(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", + data_len, client->addr, cmd, ret); + else + dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n", + data_len, client->addr, cmd, ret); + /* 2 transfers must have completed successfully */ + } else if (ret == 2) { + memcpy(data, buffer, data_len); + ret = 0; + } else { + ret = -EIO; + } + + kfree(buffer); + return ret; +} + +static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, + u8 cmd, u8 *data, u8 data_len) +{ + + struct i2c_msg msgs[1]; + u8 *buffer; + int ret = AE_OK; + + buffer = kzalloc(data_len + 1, GFP_KERNEL); + if (!buffer) + return AE_NO_MEMORY; + + buffer[0] = cmd; + memcpy(buffer + 1, data, data_len); + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = data_len + 1; + msgs[0].buf = buffer; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + + kfree(buffer); + + if (ret < 0) { + dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); + return ret; + } + + /* 1 transfer must have completed successfully */ + return (ret == 1) ? 0 : -EIO; +} + +static acpi_status +i2c_acpi_space_handler(u32 function, acpi_physical_address command, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct gsb_buffer *gsb = (struct gsb_buffer *)value64; + struct i2c_acpi_handler_data *data = handler_context; + struct acpi_connection_info *info = &data->info; + struct acpi_resource_i2c_serialbus *sb; + struct i2c_adapter *adapter = data->adapter; + struct i2c_client *client; + struct acpi_resource *ares; + u32 accessor_type = function >> 16; + u8 action = function & ACPI_IO_MASK; + acpi_status ret; + int status; + + ret = acpi_buffer_to_resource(info->connection, info->length, &ares); + if (ACPI_FAILURE(ret)) + return ret; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) { + ret = AE_NO_MEMORY; + goto err; + } + + if (!value64 || !i2c_acpi_get_i2c_resource(ares, &sb)) { + ret = AE_BAD_PARAMETER; + goto err; + } + + client->adapter = adapter; + client->addr = sb->slave_address; + + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + client->flags |= I2C_CLIENT_TEN; + + switch (accessor_type) { + case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: + if (action == ACPI_READ) { + status = i2c_smbus_read_byte(client); + if (status >= 0) { + gsb->bdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_byte(client, gsb->bdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_BYTE: + if (action == ACPI_READ) { + status = i2c_smbus_read_byte_data(client, command); + if (status >= 0) { + gsb->bdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_byte_data(client, command, + gsb->bdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_WORD: + if (action == ACPI_READ) { + status = i2c_smbus_read_word_data(client, command); + if (status >= 0) { + gsb->wdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_word_data(client, command, + gsb->wdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_BLOCK: + if (action == ACPI_READ) { + status = i2c_smbus_read_block_data(client, command, + gsb->data); + if (status >= 0) { + gsb->len = status; + status = 0; + } + } else { + status = i2c_smbus_write_block_data(client, command, + gsb->len, gsb->data); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: + if (action == ACPI_READ) { + status = acpi_gsb_i2c_read_bytes(client, command, + gsb->data, info->access_length); + } else { + status = acpi_gsb_i2c_write_bytes(client, command, + gsb->data, info->access_length); + } + break; + + default: + dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", + accessor_type, client->addr); + ret = AE_BAD_PARAMETER; + goto err; + } + + gsb->status = status; + + err: + kfree(client); + ACPI_FREE(ares); + return ret; +} + + +int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) +{ + acpi_handle handle; + struct i2c_acpi_handler_data *data; + acpi_status status; + + if (!adapter->dev.parent) + return -ENODEV; + + handle = ACPI_HANDLE(adapter->dev.parent); + + if (!handle) + return -ENODEV; + + data = kzalloc(sizeof(struct i2c_acpi_handler_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->adapter = adapter; + status = acpi_bus_attach_private_data(handle, (void *)data); + if (ACPI_FAILURE(status)) { + kfree(data); + return -ENOMEM; + } + + status = acpi_install_address_space_handler(handle, + ACPI_ADR_SPACE_GSBUS, + &i2c_acpi_space_handler, + NULL, + data); + if (ACPI_FAILURE(status)) { + dev_err(&adapter->dev, "Error installing i2c space handler\n"); + acpi_bus_detach_private_data(handle); + kfree(data); + return -ENOMEM; + } + + acpi_walk_dep_device_list(handle); + return 0; +} + +void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) +{ + acpi_handle handle; + struct i2c_acpi_handler_data *data; + acpi_status status; + + if (!adapter->dev.parent) + return; + + handle = ACPI_HANDLE(adapter->dev.parent); + + if (!handle) + return; + + acpi_remove_address_space_handler(handle, + ACPI_ADR_SPACE_GSBUS, + &i2c_acpi_space_handler); + + status = acpi_bus_get_private_data(handle, (void **)&data); + if (ACPI_SUCCESS(status)) + kfree(data); + + acpi_bus_detach_private_data(handle); +} +#endif /* CONFIG_ACPI_I2C_OPREGION */ diff --git a/t/tree/drivers/i2c/i2c-core-base.c b/t/tree/drivers/i2c/i2c-core-base.c new file mode 100644 index 00000000..5f6a4985 --- /dev/null +++ b/t/tree/drivers/i2c/i2c-core-base.c @@ -0,0 +1,2399 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core + * + * Copyright (C) 1995-99 Simon G. Vogl + * With some changes from Kyösti Mälkki + * Mux support by Rodolfo Giometti and + * Michael Lawnick + * + * Copyright (C) 2013-2017 Wolfram Sang + */ + +#define pr_fmt(fmt) "i2c-core: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +#define CREATE_TRACE_POINTS +#include + +#define I2C_ADDR_OFFSET_TEN_BIT 0xa000 +#define I2C_ADDR_OFFSET_SLAVE 0x1000 + +#define I2C_ADDR_7BITS_MAX 0x77 +#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1) + +#define I2C_ADDR_DEVICE_ID 0x7c + +/* + * core_lock protects i2c_adapter_idr, and guarantees that device detection, + * deletion of detected devices are serialized + */ +static DEFINE_MUTEX(core_lock); +static DEFINE_IDR(i2c_adapter_idr); + +static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); + +static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key); +static bool is_registered; + +int i2c_transfer_trace_reg(void) +{ + static_branch_inc(&i2c_trace_msg_key); + return 0; +} + +void i2c_transfer_trace_unreg(void) +{ + static_branch_dec(&i2c_trace_msg_key); +} + +const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, + const struct i2c_client *client) +{ + if (!(id && client)) + return NULL; + + while (id->name[0]) { + if (strcmp(client->name, id->name) == 0) + return id; + id++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(i2c_match_id); + +static int i2c_device_match(struct device *dev, struct device_driver *drv) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + + /* Attempt an OF style match */ + if (i2c_of_match_device(drv->of_match_table, client)) + return 1; + + /* Then ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + driver = to_i2c_driver(drv); + + /* Finally an I2C match */ + if (i2c_match_id(driver->id_table, client)) + return 1; + + return 0; +} + +static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + + rc = of_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + + return add_uevent_var(env, "MODALIAS=%s%s", I2C_MODULE_PREFIX, client->name); +} + +/* i2c bus recovery routines */ +static int get_scl_gpio_value(struct i2c_adapter *adap) +{ + return gpiod_get_value_cansleep(adap->bus_recovery_info->scl_gpiod); +} + +static void set_scl_gpio_value(struct i2c_adapter *adap, int val) +{ + gpiod_set_value_cansleep(adap->bus_recovery_info->scl_gpiod, val); +} + +static int get_sda_gpio_value(struct i2c_adapter *adap) +{ + return gpiod_get_value_cansleep(adap->bus_recovery_info->sda_gpiod); +} + +static void set_sda_gpio_value(struct i2c_adapter *adap, int val) +{ + gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val); +} + +static int i2c_generic_bus_free(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + int ret = -EOPNOTSUPP; + + if (bri->get_bus_free) + ret = bri->get_bus_free(adap); + else if (bri->get_sda) + ret = bri->get_sda(adap); + + if (ret < 0) + return ret; + + return ret ? 0 : -EBUSY; +} + +/* + * We are generating clock pulses. ndelay() determines durating of clk pulses. + * We will generate clock with rate 100 KHz and so duration of both clock levels + * is: delay in ns = (10^6 / 100) / 2 + */ +#define RECOVERY_NDELAY 5000 +#define RECOVERY_CLK_CNT 9 + +int i2c_generic_scl_recovery(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + int i = 0, scl = 1, ret = 0; + + if (bri->prepare_recovery) + bri->prepare_recovery(adap); + + /* + * If we can set SDA, we will always create a STOP to ensure additional + * pulses will do no harm. This is achieved by letting SDA follow SCL + * half a cycle later. Check the 'incomplete_write_byte' fault injector + * for details. + */ + bri->set_scl(adap, scl); + ndelay(RECOVERY_NDELAY / 2); + if (bri->set_sda) + bri->set_sda(adap, scl); + ndelay(RECOVERY_NDELAY / 2); + + /* + * By this time SCL is high, as we need to give 9 falling-rising edges + */ + while (i++ < RECOVERY_CLK_CNT * 2) { + if (scl) { + /* SCL shouldn't be low here */ + if (!bri->get_scl(adap)) { + dev_err(&adap->dev, + "SCL is stuck low, exit recovery\n"); + ret = -EBUSY; + break; + } + } + + scl = !scl; + bri->set_scl(adap, scl); + /* Creating STOP again, see above */ + ndelay(RECOVERY_NDELAY / 2); + if (bri->set_sda) + bri->set_sda(adap, scl); + ndelay(RECOVERY_NDELAY / 2); + + if (scl) { + ret = i2c_generic_bus_free(adap); + if (ret == 0) + break; + } + } + + /* If we can't check bus status, assume recovery worked */ + if (ret == -EOPNOTSUPP) + ret = 0; + + if (bri->unprepare_recovery) + bri->unprepare_recovery(adap); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery); + +int i2c_recover_bus(struct i2c_adapter *adap) +{ + if (!adap->bus_recovery_info) + return -EOPNOTSUPP; + + dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); + return adap->bus_recovery_info->recover_bus(adap); +} +EXPORT_SYMBOL_GPL(i2c_recover_bus); + +static void i2c_init_recovery(struct i2c_adapter *adap) +{ + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; + char *err_str; + + if (!bri) + return; + + if (!bri->recover_bus) { + err_str = "no recover_bus() found"; + goto err; + } + + if (bri->scl_gpiod && bri->recover_bus == i2c_generic_scl_recovery) { + bri->get_scl = get_scl_gpio_value; + bri->set_scl = set_scl_gpio_value; + if (bri->sda_gpiod) { + bri->get_sda = get_sda_gpio_value; + /* FIXME: add proper flag instead of '0' once available */ + if (gpiod_get_direction(bri->sda_gpiod) == 0) + bri->set_sda = set_sda_gpio_value; + } + return; + } + + if (bri->recover_bus == i2c_generic_scl_recovery) { + /* Generic SCL recovery */ + if (!bri->set_scl || !bri->get_scl) { + err_str = "no {get|set}_scl() found"; + goto err; + } + if (!bri->set_sda && !bri->get_sda) { + err_str = "either get_sda() or set_sda() needed"; + goto err; + } + } + + return; + err: + dev_err(&adap->dev, "Not using recovery: %s\n", err_str); + adap->bus_recovery_info = NULL; +} + +static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client) +{ + struct i2c_adapter *adap = client->adapter; + unsigned int irq; + + if (!adap->host_notify_domain) + return -ENXIO; + + if (client->flags & I2C_CLIENT_TEN) + return -EINVAL; + + irq = irq_create_mapping(adap->host_notify_domain, client->addr); + + return irq > 0 ? irq : -ENXIO; +} + +static int i2c_device_probe(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + int status; + + if (!client) + return 0; + + driver = to_i2c_driver(dev->driver); + + client->irq = client->init_irq; + + if (!client->irq && !driver->disable_i2c_core_irq_mapping) { + int irq = -ENOENT; + + if (client->flags & I2C_CLIENT_HOST_NOTIFY) { + dev_dbg(dev, "Using Host Notify IRQ\n"); + /* Keep adapter active when Host Notify is required */ + pm_runtime_get_sync(&client->adapter->dev); + irq = i2c_smbus_host_notify_to_irq(client); + } else if (dev->of_node) { + irq = of_irq_get_byname(dev->of_node, "irq"); + if (irq == -EINVAL || irq == -ENODATA) + irq = of_irq_get(dev->of_node, 0); + } else if (ACPI_COMPANION(dev)) { + irq = i2c_acpi_get_irq(client); + } + if (irq == -EPROBE_DEFER) + return irq; + + if (irq < 0) + irq = 0; + + client->irq = irq; + } + + /* + * An I2C ID table is not mandatory, if and only if, a suitable OF + * or ACPI ID table is supplied for the probing device. + */ + if (!driver->id_table && + !i2c_acpi_match_device(dev->driver->acpi_match_table, client) && + !i2c_of_match_device(dev->driver->of_match_table, client)) + return -ENODEV; + + if (client->flags & I2C_CLIENT_WAKE) { + int wakeirq; + + wakeirq = of_irq_get_byname(dev->of_node, "wakeup"); + if (wakeirq == -EPROBE_DEFER) + return wakeirq; + + device_init_wakeup(&client->dev, true); + + if (wakeirq > 0 && wakeirq != client->irq) + status = dev_pm_set_dedicated_wake_irq(dev, wakeirq); + else if (client->irq > 0) + status = dev_pm_set_wake_irq(dev, client->irq); + else + status = 0; + + if (status) + dev_warn(&client->dev, "failed to set up wakeup irq\n"); + } + + dev_dbg(dev, "probe\n"); + + status = of_clk_set_defaults(dev->of_node, false); + if (status < 0) + goto err_clear_wakeup_irq; + + status = dev_pm_domain_attach(&client->dev, true); + if (status) + goto err_clear_wakeup_irq; + + /* + * When there are no more users of probe(), + * rename probe_new to probe. + */ + if (driver->probe_new) + status = driver->probe_new(client); + else if (driver->probe) + status = driver->probe(client, + i2c_match_id(driver->id_table, client)); + else + status = -EINVAL; + + if (status) + goto err_detach_pm_domain; + + return 0; + +err_detach_pm_domain: + dev_pm_domain_detach(&client->dev, true); +err_clear_wakeup_irq: + dev_pm_clear_wake_irq(&client->dev); + device_init_wakeup(&client->dev, false); + return status; +} + +static int i2c_device_remove(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + int status = 0; + + if (!client || !dev->driver) + return 0; + + driver = to_i2c_driver(dev->driver); + if (driver->remove) { + dev_dbg(dev, "remove\n"); + status = driver->remove(client); + } + + dev_pm_domain_detach(&client->dev, true); + + dev_pm_clear_wake_irq(&client->dev); + device_init_wakeup(&client->dev, false); + + client->irq = 0; + if (client->flags & I2C_CLIENT_HOST_NOTIFY) + pm_runtime_put(&client->adapter->dev); + + return status; +} + +static void i2c_device_shutdown(struct device *dev) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; + + if (!client || !dev->driver) + return; + driver = to_i2c_driver(dev->driver); + if (driver->shutdown) + driver->shutdown(client); +} + +static void i2c_client_dev_release(struct device *dev) +{ + kfree(to_i2c_client(dev)); +} + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", dev->type == &i2c_client_type ? + to_i2c_client(dev)->name : to_i2c_adapter(dev)->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static ssize_t +show_modalias(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int len; + + len = of_device_modalias(dev, buf, PAGE_SIZE); + if (len != -ENODEV) + return len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); + if (len != -ENODEV) + return len; + + return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name); +} +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct attribute *i2c_dev_attrs[] = { + &dev_attr_name.attr, + /* modalias helps coldplug: modprobe $(cat .../modalias) */ + &dev_attr_modalias.attr, + NULL +}; +ATTRIBUTE_GROUPS(i2c_dev); + +struct bus_type i2c_bus_type = { + .name = "i2c", + .match = i2c_device_match, + .probe = i2c_device_probe, + .remove = i2c_device_remove, + .shutdown = i2c_device_shutdown, +}; +EXPORT_SYMBOL_GPL(i2c_bus_type); + +struct device_type i2c_client_type = { + .groups = i2c_dev_groups, + .uevent = i2c_device_uevent, + .release = i2c_client_dev_release, +}; +EXPORT_SYMBOL_GPL(i2c_client_type); + + +/** + * i2c_verify_client - return parameter as i2c_client, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_client. + */ +struct i2c_client *i2c_verify_client(struct device *dev) +{ + return (dev->type == &i2c_client_type) + ? to_i2c_client(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_client); + + +/* Return a unique address which takes the flags of the client into account */ +static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client) +{ + unsigned short addr = client->addr; + + /* For some client flags, add an arbitrary offset to avoid collisions */ + if (client->flags & I2C_CLIENT_TEN) + addr |= I2C_ADDR_OFFSET_TEN_BIT; + + if (client->flags & I2C_CLIENT_SLAVE) + addr |= I2C_ADDR_OFFSET_SLAVE; + + return addr; +} + +/* This is a permissive address validity check, I2C address map constraints + * are purposely not enforced, except for the general call address. */ +static int i2c_check_addr_validity(unsigned int addr, unsigned short flags) +{ + if (flags & I2C_CLIENT_TEN) { + /* 10-bit address, all values are valid */ + if (addr > 0x3ff) + return -EINVAL; + } else { + /* 7-bit address, reject the general call address */ + if (addr == 0x00 || addr > 0x7f) + return -EINVAL; + } + return 0; +} + +/* And this is a strict address validity check, used when probing. If a + * device uses a reserved address, then it shouldn't be probed. 7-bit + * addressing is assumed, 10-bit address devices are rare and should be + * explicitly enumerated. */ +int i2c_check_7bit_addr_validity_strict(unsigned short addr) +{ + /* + * Reserved addresses per I2C specification: + * 0x00 General call address / START byte + * 0x01 CBUS address + * 0x02 Reserved for different bus format + * 0x03 Reserved for future purposes + * 0x04-0x07 Hs-mode master code + * 0x78-0x7b 10-bit slave addressing + * 0x7c-0x7f Reserved for future purposes + */ + if (addr < 0x08 || addr > 0x77) + return -EINVAL; + return 0; +} + +static int __i2c_check_addr_busy(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + int addr = *(int *)addrp; + + if (client && i2c_encode_flags_to_addr(client) == addr) + return -EBUSY; + return 0; +} + +/* walk up mux tree */ +static int i2c_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, + __i2c_check_addr_busy); + + if (!result && parent) + result = i2c_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2c_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2c_check_mux_children); + else + result = __i2c_check_addr_busy(dev, addrp); + + return result; +} + +static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2c_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2c_check_mux_children); + + return result; +} + +/** + * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree + */ +static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter)); +} + +/** + * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT + * trylocks only this branch in the adapter tree + */ +static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + return rt_mutex_trylock(&adapter->bus_lock); +} + +/** + * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree + */ +static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + rt_mutex_unlock(&adapter->bus_lock); +} + +static void i2c_dev_set_name(struct i2c_adapter *adap, + struct i2c_client *client, + struct i2c_board_info const *info) +{ + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + + if (info && info->dev_name) { + dev_set_name(&client->dev, "i2c-%s", info->dev_name); + return; + } + + if (adev) { + dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev)); + return; + } + + dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), + i2c_encode_flags_to_addr(client)); +} + +int i2c_dev_irq_from_resources(const struct resource *resources, + unsigned int num_resources) +{ + struct irq_data *irqd; + int i; + + for (i = 0; i < num_resources; i++) { + const struct resource *r = &resources[i]; + + if (resource_type(r) != IORESOURCE_IRQ) + continue; + + if (r->flags & IORESOURCE_BITS) { + irqd = irq_get_irq_data(r->start); + if (!irqd) + break; + + irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); + } + + return r->start; + } + + return 0; +} + +/** + * i2c_new_client_device - instantiate an i2c device + * @adap: the adapter managing the device + * @info: describes one I2C device; bus_num is ignored + * Context: can sleep + * + * Create an i2c device. Binding is handled through driver model + * probe()/remove() methods. A driver may be bound to this device when we + * return from this function, or any later moment (e.g. maybe hotplugging will + * load the driver module). This call is not appropriate for use by mainboard + * initialization logic, which usually runs during an arch_initcall() long + * before any i2c_adapter could exist. + * + * This returns the new i2c client, which may be saved for later use with + * i2c_unregister_device(); or an ERR_PTR to describe the error. + */ +struct i2c_client * +i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +{ + struct i2c_client *client; + int status; + + client = kzalloc(sizeof *client, GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->adapter = adap; + + client->dev.platform_data = info->platform_data; + client->flags = info->flags; + client->addr = info->addr; + + client->init_irq = info->irq; + if (!client->init_irq) + client->init_irq = i2c_dev_irq_from_resources(info->resources, + info->num_resources); + + strlcpy(client->name, info->type, sizeof(client->name)); + + status = i2c_check_addr_validity(client->addr, client->flags); + if (status) { + dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", + client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); + goto out_err_silent; + } + + /* Check for address business */ + status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); + if (status) + goto out_err; + + client->dev.parent = &client->adapter->dev; + client->dev.bus = &i2c_bus_type; + client->dev.type = &i2c_client_type; + client->dev.of_node = of_node_get(info->of_node); + client->dev.fwnode = info->fwnode; + + i2c_dev_set_name(adap, client, info); + + if (info->properties) { + status = device_add_properties(&client->dev, info->properties); + if (status) { + dev_err(&adap->dev, + "Failed to add properties to client %s: %d\n", + client->name, status); + goto out_err_put_of_node; + } + } + + status = device_register(&client->dev); + if (status) + goto out_free_props; + + dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", + client->name, dev_name(&client->dev)); + + return client; + +out_free_props: + if (info->properties) + device_remove_properties(&client->dev); +out_err_put_of_node: + of_node_put(info->of_node); +out_err: + dev_err(&adap->dev, + "Failed to register i2c client %s at 0x%02x (%d)\n", + client->name, client->addr, status); +out_err_silent: + kfree(client); + return ERR_PTR(status); +} +EXPORT_SYMBOL_GPL(i2c_new_client_device); + +/** + * i2c_new_device - instantiate an i2c device + * @adap: the adapter managing the device + * @info: describes one I2C device; bus_num is ignored + * Context: can sleep + * + * This deprecated function has the same functionality as + * @i2c_new_client_device, it just returns NULL instead of an ERR_PTR in case of + * an error for compatibility with current I2C API. It will be removed once all + * users are converted. + * + * This returns the new i2c client, which may be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client * +i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +{ + struct i2c_client *ret; + + ret = i2c_new_client_device(adap, info); + return IS_ERR(ret) ? NULL : ret; +} +EXPORT_SYMBOL_GPL(i2c_new_device); + + +/** + * i2c_unregister_device - reverse effect of i2c_new_device() + * @client: value returned from i2c_new_device() + * Context: can sleep + */ +void i2c_unregister_device(struct i2c_client *client) +{ + if (IS_ERR_OR_NULL(client)) + return; + + if (client->dev.of_node) { + of_node_clear_flag(client->dev.of_node, OF_POPULATED); + of_node_put(client->dev.of_node); + } + + if (ACPI_COMPANION(&client->dev)) + acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev)); + device_unregister(&client->dev); +} +EXPORT_SYMBOL_GPL(i2c_unregister_device); + + +static const struct i2c_device_id dummy_id[] = { + { "dummy", 0 }, + { }, +}; + +static int dummy_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return 0; +} + +static int dummy_remove(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver dummy_driver = { + .driver.name = "dummy", + .probe = dummy_probe, + .remove = dummy_remove, + .id_table = dummy_id, +}; + +/** + * i2c_new_dummy_device - return a new i2c device bound to a dummy driver + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * Context: can sleep + * + * This returns an I2C client bound to the "dummy" driver, intended for use + * with devices that consume multiple addresses. Examples of such chips + * include various EEPROMS (like 24c04 and 24c08 models). + * + * These dummy devices have two main uses. First, most I2C and SMBus calls + * except i2c_transfer() need a client handle; the dummy will be that handle. + * And second, this prevents the specified address from being bound to a + * different driver. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or an ERR_PTR to describe the error. + */ +struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address) +{ + struct i2c_board_info info = { + I2C_BOARD_INFO("dummy", address), + }; + + return i2c_new_client_device(adapter, &info); +} +EXPORT_SYMBOL_GPL(i2c_new_dummy_device); + +/** + * i2c_new_dummy - return a new i2c device bound to a dummy driver + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * Context: can sleep + * + * This deprecated function has the same functionality as @i2c_new_dummy_device, + * it just returns NULL instead of an ERR_PTR in case of an error for + * compatibility with current I2C API. It will be removed once all users are + * converted. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) +{ + struct i2c_client *ret; + + ret = i2c_new_dummy_device(adapter, address); + return IS_ERR(ret) ? NULL : ret; +} +EXPORT_SYMBOL_GPL(i2c_new_dummy); + +struct i2c_dummy_devres { + struct i2c_client *client; +}; + +static void devm_i2c_release_dummy(struct device *dev, void *res) +{ + struct i2c_dummy_devres *this = res; + + i2c_unregister_device(this->client); +} + +/** + * devm_i2c_new_dummy_device - return a new i2c device bound to a dummy driver + * @dev: device the managed resource is bound to + * @adapter: the adapter managing the device + * @address: seven bit address to be used + * Context: can sleep + * + * This is the device-managed version of @i2c_new_dummy_device. It returns the + * new i2c client or an ERR_PTR in case of an error. + */ +struct i2c_client *devm_i2c_new_dummy_device(struct device *dev, + struct i2c_adapter *adapter, + u16 address) +{ + struct i2c_dummy_devres *dr; + struct i2c_client *client; + + dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + client = i2c_new_dummy_device(adapter, address); + if (IS_ERR(client)) { + devres_free(dr); + } else { + dr->client = client; + devres_add(dev, dr); + } + + return client; +} +EXPORT_SYMBOL_GPL(devm_i2c_new_dummy_device); + +/** + * i2c_new_ancillary_device - Helper to get the instantiated secondary address + * and create the associated device + * @client: Handle to the primary client + * @name: Handle to specify which secondary address to get + * @default_addr: Used as a fallback if no secondary address was specified + * Context: can sleep + * + * I2C clients can be composed of multiple I2C slaves bound together in a single + * component. The I2C client driver then binds to the master I2C slave and needs + * to create I2C dummy clients to communicate with all the other slaves. + * + * This function creates and returns an I2C dummy client whose I2C address is + * retrieved from the platform firmware based on the given slave name. If no + * address is specified by the firmware default_addr is used. + * + * On DT-based platforms the address is retrieved from the "reg" property entry + * cell whose "reg-names" value matches the slave name. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or an ERR_PTR to describe the error. + */ +struct i2c_client *i2c_new_ancillary_device(struct i2c_client *client, + const char *name, + u16 default_addr) +{ + struct device_node *np = client->dev.of_node; + u32 addr = default_addr; + int i; + + if (np) { + i = of_property_match_string(np, "reg-names", name); + if (i >= 0) + of_property_read_u32_index(np, "reg", i, &addr); + } + + dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr); + return i2c_new_dummy_device(client->adapter, addr); +} +EXPORT_SYMBOL_GPL(i2c_new_ancillary_device); + +/* ------------------------------------------------------------------------- */ + +/* I2C bus adapters -- one roots each I2C or SMBUS segment */ + +static void i2c_adapter_dev_release(struct device *dev) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + complete(&adap->dev_released); +} + +unsigned int i2c_adapter_depth(struct i2c_adapter *adapter) +{ + unsigned int depth = 0; + + while ((adapter = i2c_parent_is_i2c_adapter(adapter))) + depth++; + + WARN_ONCE(depth >= MAX_LOCKDEP_SUBCLASSES, + "adapter depth exceeds lockdep subclass limit\n"); + + return depth; +} +EXPORT_SYMBOL_GPL(i2c_adapter_depth); + +/* + * Let users instantiate I2C devices through sysfs. This can be used when + * platform initialization code doesn't contain the proper data for + * whatever reason. Also useful for drivers that do device detection and + * detection fails, either because the device uses an unexpected address, + * or this is a compatible device with different ID register values. + * + * Parameter checking may look overzealous, but we really don't want + * the user to provide incorrect parameters. + */ +static ssize_t +i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + struct i2c_board_info info; + struct i2c_client *client; + char *blank, end; + int res; + + memset(&info, 0, sizeof(struct i2c_board_info)); + + blank = strchr(buf, ' '); + if (!blank) { + dev_err(dev, "%s: Missing parameters\n", "new_device"); + return -EINVAL; + } + if (blank - buf > I2C_NAME_SIZE - 1) { + dev_err(dev, "%s: Invalid device name\n", "new_device"); + return -EINVAL; + } + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ + res = sscanf(++blank, "%hi%c", &info.addr, &end); + if (res < 1) { + dev_err(dev, "%s: Can't parse I2C address\n", "new_device"); + return -EINVAL; + } + if (res > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "new_device"); + return -EINVAL; + } + + if ((info.addr & I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) { + info.addr &= ~I2C_ADDR_OFFSET_TEN_BIT; + info.flags |= I2C_CLIENT_TEN; + } + + if (info.addr & I2C_ADDR_OFFSET_SLAVE) { + info.addr &= ~I2C_ADDR_OFFSET_SLAVE; + info.flags |= I2C_CLIENT_SLAVE; + } + + client = i2c_new_client_device(adap, &info); + if (IS_ERR(client)) + return PTR_ERR(client); + + /* Keep track of the added device */ + mutex_lock(&adap->userspace_clients_lock); + list_add_tail(&client->detected, &adap->userspace_clients); + mutex_unlock(&adap->userspace_clients_lock); + dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", + info.type, info.addr); + + return count; +} +static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); + +/* + * And of course let the users delete the devices they instantiated, if + * they got it wrong. This interface can only be used to delete devices + * instantiated by i2c_sysfs_new_device above. This guarantees that we + * don't delete devices to which some kernel code still has references. + * + * Parameter checking may look overzealous, but we really don't want + * the user to delete the wrong device. + */ +static ssize_t +i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + struct i2c_client *client, *next; + unsigned short addr; + char end; + int res; + + /* Parse parameters, reject extra parameters */ + res = sscanf(buf, "%hi%c", &addr, &end); + if (res < 1) { + dev_err(dev, "%s: Can't parse I2C address\n", "delete_device"); + return -EINVAL; + } + if (res > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "delete_device"); + return -EINVAL; + } + + /* Make sure the device was added through sysfs */ + res = -ENOENT; + mutex_lock_nested(&adap->userspace_clients_lock, + i2c_adapter_depth(adap)); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + if (i2c_encode_flags_to_addr(client) == addr) { + dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", + "delete_device", client->name, client->addr); + + list_del(&client->detected); + i2c_unregister_device(client); + res = count; + break; + } + } + mutex_unlock(&adap->userspace_clients_lock); + + if (res < 0) + dev_err(dev, "%s: Can't find device in list\n", + "delete_device"); + return res; +} +static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL, + i2c_sysfs_delete_device); + +static struct attribute *i2c_adapter_attrs[] = { + &dev_attr_name.attr, + &dev_attr_new_device.attr, + &dev_attr_delete_device.attr, + NULL +}; +ATTRIBUTE_GROUPS(i2c_adapter); + +struct device_type i2c_adapter_type = { + .groups = i2c_adapter_groups, + .release = i2c_adapter_dev_release, +}; +EXPORT_SYMBOL_GPL(i2c_adapter_type); + +/** + * i2c_verify_adapter - return parameter as i2c_adapter or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-I2C device as an i2c_adapter. + */ +struct i2c_adapter *i2c_verify_adapter(struct device *dev) +{ + return (dev->type == &i2c_adapter_type) + ? to_i2c_adapter(dev) + : NULL; +} +EXPORT_SYMBOL(i2c_verify_adapter); + +#ifdef CONFIG_I2C_COMPAT +static struct class_compat *i2c_adapter_compat_class; +#endif + +static void i2c_scan_static_board_info(struct i2c_adapter *adapter) +{ + struct i2c_devinfo *devinfo; + + down_read(&__i2c_board_lock); + list_for_each_entry(devinfo, &__i2c_board_list, list) { + if (devinfo->busnum == adapter->nr + && !i2c_new_device(adapter, + &devinfo->board_info)) + dev_err(&adapter->dev, + "Can't create device at 0x%02x\n", + devinfo->board_info.addr); + } + up_read(&__i2c_board_lock); +} + +static int i2c_do_add_adapter(struct i2c_driver *driver, + struct i2c_adapter *adap) +{ + /* Detect supported devices on that bus, and instantiate them */ + i2c_detect(adap, driver); + + return 0; +} + +static int __process_new_adapter(struct device_driver *d, void *data) +{ + return i2c_do_add_adapter(to_i2c_driver(d), data); +} + +static const struct i2c_lock_operations i2c_adapter_lock_ops = { + .lock_bus = i2c_adapter_lock_bus, + .trylock_bus = i2c_adapter_trylock_bus, + .unlock_bus = i2c_adapter_unlock_bus, +}; + +static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap) +{ + struct irq_domain *domain = adap->host_notify_domain; + irq_hw_number_t hwirq; + + if (!domain) + return; + + for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++) + irq_dispose_mapping(irq_find_mapping(domain, hwirq)); + + irq_domain_remove(domain); + adap->host_notify_domain = NULL; +} + +static int i2c_host_notify_irq_map(struct irq_domain *h, + unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq); + + return 0; +} + +static const struct irq_domain_ops i2c_host_notify_irq_ops = { + .map = i2c_host_notify_irq_map, +}; + +static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap) +{ + struct irq_domain *domain; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY)) + return 0; + + domain = irq_domain_create_linear(adap->dev.fwnode, + I2C_ADDR_7BITS_COUNT, + &i2c_host_notify_irq_ops, adap); + if (!domain) + return -ENOMEM; + + adap->host_notify_domain = domain; + + return 0; +} + +/** + * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct + * I2C client. + * @adap: the adapter + * @addr: the I2C address of the notifying device + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the Host Notify IRQ. + */ +int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) +{ + int irq; + + if (!adap) + return -EINVAL; + + irq = irq_find_mapping(adap->host_notify_domain, addr); + if (irq <= 0) + return -ENXIO; + + generic_handle_irq(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + +static int i2c_register_adapter(struct i2c_adapter *adap) +{ + int res = -EINVAL; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) { + res = -EAGAIN; + goto out_list; + } + + /* Sanity checks */ + if (WARN(!adap->name[0], "i2c adapter has no name")) + goto out_list; + + if (!adap->algo) { + pr_err("adapter '%s': no algo supplied!\n", adap->name); + goto out_list; + } + + if (!adap->lock_ops) + adap->lock_ops = &i2c_adapter_lock_ops; + + adap->locked_flags = 0; + rt_mutex_init(&adap->bus_lock); + rt_mutex_init(&adap->mux_lock); + mutex_init(&adap->userspace_clients_lock); + INIT_LIST_HEAD(&adap->userspace_clients); + + /* Set default timeout to 1 second if not already set */ + if (adap->timeout == 0) + adap->timeout = HZ; + + /* register soft irqs for Host Notify */ + res = i2c_setup_host_notify_irq_domain(adap); + if (res) { + pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", + adap->name, res); + goto out_list; + } + + dev_set_name(&adap->dev, "i2c-%d", adap->nr); + adap->dev.bus = &i2c_bus_type; + adap->dev.type = &i2c_adapter_type; + res = device_register(&adap->dev); + if (res) { + pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); + goto out_list; + } + + res = of_i2c_setup_smbus_alert(adap); + if (res) + goto out_reg; + + dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); + + pm_runtime_no_callbacks(&adap->dev); + pm_suspend_ignore_children(&adap->dev, true); + pm_runtime_enable(&adap->dev); + +#ifdef CONFIG_I2C_COMPAT + res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); + if (res) + dev_warn(&adap->dev, + "Failed to create compatibility class link\n"); +#endif + + i2c_init_recovery(adap); + + /* create pre-declared device nodes */ + of_i2c_register_devices(adap); + i2c_acpi_register_devices(adap); + i2c_acpi_install_space_handler(adap); + + if (adap->nr < __i2c_first_dynamic_bus_num) + i2c_scan_static_board_info(adap); + + /* Notify drivers */ + mutex_lock(&core_lock); + bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); + mutex_unlock(&core_lock); + + return 0; + +out_reg: + init_completion(&adap->dev_released); + device_unregister(&adap->dev); + wait_for_completion(&adap->dev_released); +out_list: + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + return res; +} + +/** + * __i2c_add_numbered_adapter - i2c_add_numbered_adapter where nr is never -1 + * @adap: the adapter to register (with adap->nr initialized) + * Context: can sleep + * + * See i2c_add_numbered_adapter() for details. + */ +static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) +{ + int id; + + mutex_lock(&core_lock); + id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL); + mutex_unlock(&core_lock); + if (WARN(id < 0, "couldn't get idr")) + return id == -ENOSPC ? -EBUSY : id; + + return i2c_register_adapter(adap); +} + +/** + * i2c_add_adapter - declare i2c adapter, use dynamic bus number + * @adapter: the adapter to add + * Context: can sleep + * + * This routine is used to declare an I2C adapter when its bus number + * doesn't matter or when its bus number is specified by an dt alias. + * Examples of bases when the bus number doesn't matter: I2C adapters + * dynamically added by USB links or PCI plugin cards. + * + * When this returns zero, a new bus number was allocated and stored + * in adap->nr, and the specified adapter became available for clients. + * Otherwise, a negative errno value is returned. + */ +int i2c_add_adapter(struct i2c_adapter *adapter) +{ + struct device *dev = &adapter->dev; + int id; + + if (dev->of_node) { + id = of_alias_get_id(dev->of_node, "i2c"); + if (id >= 0) { + adapter->nr = id; + return __i2c_add_numbered_adapter(adapter); + } + } + + mutex_lock(&core_lock); + id = idr_alloc(&i2c_adapter_idr, adapter, + __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); + mutex_unlock(&core_lock); + if (WARN(id < 0, "couldn't get idr")) + return id; + + adapter->nr = id; + + return i2c_register_adapter(adapter); +} +EXPORT_SYMBOL(i2c_add_adapter); + +/** + * i2c_add_numbered_adapter - declare i2c adapter, use static bus number + * @adap: the adapter to register (with adap->nr initialized) + * Context: can sleep + * + * This routine is used to declare an I2C adapter when its bus number + * matters. For example, use it for I2C adapters from system-on-chip CPUs, + * or otherwise built in to the system's mainboard, and where i2c_board_info + * is used to properly configure I2C devices. + * + * If the requested bus number is set to -1, then this function will behave + * identically to i2c_add_adapter, and will dynamically assign a bus number. + * + * If no devices have pre-been declared for this bus, then be sure to + * register the adapter before any dynamically allocated ones. Otherwise + * the required bus ID may not be available. + * + * When this returns zero, the specified adapter became available for + * clients using the bus number provided in adap->nr. Also, the table + * of I2C devices pre-declared using i2c_register_board_info() is scanned, + * and the appropriate driver model device nodes are created. Otherwise, a + * negative errno value is returned. + */ +int i2c_add_numbered_adapter(struct i2c_adapter *adap) +{ + if (adap->nr == -1) /* -1 means dynamically assign bus id */ + return i2c_add_adapter(adap); + + return __i2c_add_numbered_adapter(adap); +} +EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter); + +static void i2c_do_del_adapter(struct i2c_driver *driver, + struct i2c_adapter *adapter) +{ + struct i2c_client *client, *_n; + + /* Remove the devices we created ourselves as the result of hardware + * probing (using a driver's detect method) */ + list_for_each_entry_safe(client, _n, &driver->clients, detected) { + if (client->adapter == adapter) { + dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", + client->name, client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + } +} + +static int __unregister_client(struct device *dev, void *dummy) +{ + struct i2c_client *client = i2c_verify_client(dev); + if (client && strcmp(client->name, "dummy")) + i2c_unregister_device(client); + return 0; +} + +static int __unregister_dummy(struct device *dev, void *dummy) +{ + struct i2c_client *client = i2c_verify_client(dev); + i2c_unregister_device(client); + return 0; +} + +static int __process_removed_adapter(struct device_driver *d, void *data) +{ + i2c_do_del_adapter(to_i2c_driver(d), data); + return 0; +} + +/** + * i2c_del_adapter - unregister I2C adapter + * @adap: the adapter being unregistered + * Context: can sleep + * + * This unregisters an I2C adapter which was previously registered + * by @i2c_add_adapter or @i2c_add_numbered_adapter. + */ +void i2c_del_adapter(struct i2c_adapter *adap) +{ + struct i2c_adapter *found; + struct i2c_client *client, *next; + + /* First make sure that this adapter was ever added */ + mutex_lock(&core_lock); + found = idr_find(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + if (found != adap) { + pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name); + return; + } + + i2c_acpi_remove_space_handler(adap); + /* Tell drivers about this removal */ + mutex_lock(&core_lock); + bus_for_each_drv(&i2c_bus_type, NULL, adap, + __process_removed_adapter); + mutex_unlock(&core_lock); + + /* Remove devices instantiated from sysfs */ + mutex_lock_nested(&adap->userspace_clients_lock, + i2c_adapter_depth(adap)); + list_for_each_entry_safe(client, next, &adap->userspace_clients, + detected) { + dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name, + client->addr); + list_del(&client->detected); + i2c_unregister_device(client); + } + mutex_unlock(&adap->userspace_clients_lock); + + /* Detach any active clients. This can't fail, thus we do not + * check the returned value. This is a two-pass process, because + * we can't remove the dummy devices during the first pass: they + * could have been instantiated by real devices wishing to clean + * them up properly, so we give them a chance to do that first. */ + device_for_each_child(&adap->dev, NULL, __unregister_client); + device_for_each_child(&adap->dev, NULL, __unregister_dummy); + +#ifdef CONFIG_I2C_COMPAT + class_compat_remove_link(i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); +#endif + + /* device name is gone after device_unregister */ + dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); + + pm_runtime_disable(&adap->dev); + + i2c_host_notify_irq_teardown(adap); + + /* wait until all references to the device are gone + * + * FIXME: This is old code and should ideally be replaced by an + * alternative which results in decoupling the lifetime of the struct + * device from the i2c_adapter, like spi or netdev do. Any solution + * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled! + */ + init_completion(&adap->dev_released); + device_unregister(&adap->dev); + wait_for_completion(&adap->dev_released); + + /* free bus id */ + mutex_lock(&core_lock); + idr_remove(&i2c_adapter_idr, adap->nr); + mutex_unlock(&core_lock); + + /* Clear the device structure in case this adapter is ever going to be + added again */ + memset(&adap->dev, 0, sizeof(adap->dev)); +} +EXPORT_SYMBOL(i2c_del_adapter); + +/** + * i2c_parse_fw_timings - get I2C related timing parameters from firmware + * @dev: The device to scan for I2C timing properties + * @t: the i2c_timings struct to be filled with values + * @use_defaults: bool to use sane defaults derived from the I2C specification + * when properties are not found, otherwise use 0 + * + * Scan the device for the generic I2C properties describing timing parameters + * for the signal and fill the given struct with the results. If a property was + * not found and use_defaults was true, then maximum timings are assumed which + * are derived from the I2C specification. If use_defaults is not used, the + * results will be 0, so drivers can apply their own defaults later. The latter + * is mainly intended for avoiding regressions of existing drivers which want + * to switch to this function. New drivers almost always should use the defaults. + */ + +void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults) +{ + int ret; + + memset(t, 0, sizeof(*t)); + + ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz); + if (ret && use_defaults) + t->bus_freq_hz = 100000; + + ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns); + if (ret && use_defaults) { + if (t->bus_freq_hz <= 100000) + t->scl_rise_ns = 1000; + else if (t->bus_freq_hz <= 400000) + t->scl_rise_ns = 300; + else + t->scl_rise_ns = 120; + } + + ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns); + if (ret && use_defaults) { + if (t->bus_freq_hz <= 400000) + t->scl_fall_ns = 300; + else + t->scl_fall_ns = 120; + } + + device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns); + + ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns); + if (ret && use_defaults) + t->sda_fall_ns = t->scl_fall_ns; + + device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns); +} +EXPORT_SYMBOL_GPL(i2c_parse_fw_timings); + +/* ------------------------------------------------------------------------- */ + +int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data)) +{ + int res; + + mutex_lock(&core_lock); + res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); + mutex_unlock(&core_lock); + + return res; +} +EXPORT_SYMBOL_GPL(i2c_for_each_dev); + +static int __process_new_driver(struct device *dev, void *data) +{ + if (dev->type != &i2c_adapter_type) + return 0; + return i2c_do_add_adapter(data, to_i2c_adapter(dev)); +} + +/* + * An i2c_driver is used with one or more i2c_client (device) nodes to access + * i2c slave chips, on a bus instance associated with some i2c_adapter. + */ + +int i2c_register_driver(struct module *owner, struct i2c_driver *driver) +{ + int res; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) + return -EAGAIN; + + /* add the driver to the list of i2c drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &i2c_bus_type; + INIT_LIST_HEAD(&driver->clients); + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound devices. + */ + res = driver_register(&driver->driver); + if (res) + return res; + + pr_debug("driver [%s] registered\n", driver->driver.name); + + /* Walk the adapters that are already present */ + i2c_for_each_dev(driver, __process_new_driver); + + return 0; +} +EXPORT_SYMBOL(i2c_register_driver); + +static int __process_removed_driver(struct device *dev, void *data) +{ + if (dev->type == &i2c_adapter_type) + i2c_do_del_adapter(data, to_i2c_adapter(dev)); + return 0; +} + +/** + * i2c_del_driver - unregister I2C driver + * @driver: the driver being unregistered + * Context: can sleep + */ +void i2c_del_driver(struct i2c_driver *driver) +{ + i2c_for_each_dev(driver, __process_removed_driver); + + driver_unregister(&driver->driver); + pr_debug("driver [%s] unregistered\n", driver->driver.name); +} +EXPORT_SYMBOL(i2c_del_driver); + +/* ------------------------------------------------------------------------- */ + +/** + * i2c_use_client - increments the reference count of the i2c client structure + * @client: the client being referenced + * + * Each live reference to a client should be refcounted. The driver model does + * that automatically as part of driver binding, so that most drivers don't + * need to do this explicitly: they hold a reference until they're unbound + * from the device. + * + * A pointer to the client with the incremented reference counter is returned. + */ +struct i2c_client *i2c_use_client(struct i2c_client *client) +{ + if (client && get_device(&client->dev)) + return client; + return NULL; +} +EXPORT_SYMBOL(i2c_use_client); + +/** + * i2c_release_client - release a use of the i2c client structure + * @client: the client being no longer referenced + * + * Must be called when a user of a client is finished with it. + */ +void i2c_release_client(struct i2c_client *client) +{ + if (client) + put_device(&client->dev); +} +EXPORT_SYMBOL(i2c_release_client); + +struct i2c_cmd_arg { + unsigned cmd; + void *arg; +}; + +static int i2c_cmd(struct device *dev, void *_arg) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_cmd_arg *arg = _arg; + struct i2c_driver *driver; + + if (!client || !client->dev.driver) + return 0; + + driver = to_i2c_driver(client->dev.driver); + if (driver->command) + driver->command(client, arg->cmd, arg->arg); + return 0; +} + +void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg) +{ + struct i2c_cmd_arg cmd_arg; + + cmd_arg.cmd = cmd; + cmd_arg.arg = arg; + device_for_each_child(&adap->dev, &cmd_arg, i2c_cmd); +} +EXPORT_SYMBOL(i2c_clients_command); + +static int __init i2c_init(void) +{ + int retval; + + retval = of_alias_get_highest_id("i2c"); + + down_write(&__i2c_board_lock); + if (retval >= __i2c_first_dynamic_bus_num) + __i2c_first_dynamic_bus_num = retval + 1; + up_write(&__i2c_board_lock); + + retval = bus_register(&i2c_bus_type); + if (retval) + return retval; + + is_registered = true; + +#ifdef CONFIG_I2C_COMPAT + i2c_adapter_compat_class = class_compat_register("i2c-adapter"); + if (!i2c_adapter_compat_class) { + retval = -ENOMEM; + goto bus_err; + } +#endif + retval = i2c_add_driver(&dummy_driver); + if (retval) + goto class_err; + + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) + WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); + + return 0; + +class_err: +#ifdef CONFIG_I2C_COMPAT + class_compat_unregister(i2c_adapter_compat_class); +bus_err: +#endif + is_registered = false; + bus_unregister(&i2c_bus_type); + return retval; +} + +static void __exit i2c_exit(void) +{ + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier)); + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) + WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier)); + i2c_del_driver(&dummy_driver); +#ifdef CONFIG_I2C_COMPAT + class_compat_unregister(i2c_adapter_compat_class); +#endif + bus_unregister(&i2c_bus_type); + tracepoint_synchronize_unregister(); +} + +/* We must initialize early, because some subsystems register i2c drivers + * in subsys_initcall() code, but are linked (and initialized) before i2c. + */ +postcore_initcall(i2c_init); +module_exit(i2c_exit); + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +/* Check if val is exceeding the quirk IFF quirk is non 0 */ +#define i2c_quirk_exceeded(val, quirk) ((quirk) && ((val) > (quirk))) + +static int i2c_quirk_error(struct i2c_adapter *adap, struct i2c_msg *msg, char *err_msg) +{ + dev_err_ratelimited(&adap->dev, "adapter quirk: %s (addr 0x%04x, size %u, %s)\n", + err_msg, msg->addr, msg->len, + msg->flags & I2C_M_RD ? "read" : "write"); + return -EOPNOTSUPP; +} + +static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + const struct i2c_adapter_quirks *q = adap->quirks; + int max_num = q->max_num_msgs, i; + bool do_len_check = true; + + if (q->flags & I2C_AQ_COMB) { + max_num = 2; + + /* special checks for combined messages */ + if (num == 2) { + if (q->flags & I2C_AQ_COMB_WRITE_FIRST && msgs[0].flags & I2C_M_RD) + return i2c_quirk_error(adap, &msgs[0], "1st comb msg must be write"); + + if (q->flags & I2C_AQ_COMB_READ_SECOND && !(msgs[1].flags & I2C_M_RD)) + return i2c_quirk_error(adap, &msgs[1], "2nd comb msg must be read"); + + if (q->flags & I2C_AQ_COMB_SAME_ADDR && msgs[0].addr != msgs[1].addr) + return i2c_quirk_error(adap, &msgs[0], "comb msg only to same addr"); + + if (i2c_quirk_exceeded(msgs[0].len, q->max_comb_1st_msg_len)) + return i2c_quirk_error(adap, &msgs[0], "msg too long"); + + if (i2c_quirk_exceeded(msgs[1].len, q->max_comb_2nd_msg_len)) + return i2c_quirk_error(adap, &msgs[1], "msg too long"); + + do_len_check = false; + } + } + + if (i2c_quirk_exceeded(num, max_num)) + return i2c_quirk_error(adap, &msgs[0], "too many messages"); + + for (i = 0; i < num; i++) { + u16 len = msgs[i].len; + + if (msgs[i].flags & I2C_M_RD) { + if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len)) + return i2c_quirk_error(adap, &msgs[i], "msg too long"); + + if (q->flags & I2C_AQ_NO_ZERO_LEN_READ && len == 0) + return i2c_quirk_error(adap, &msgs[i], "no zero length"); + } else { + if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len)) + return i2c_quirk_error(adap, &msgs[i], "msg too long"); + + if (q->flags & I2C_AQ_NO_ZERO_LEN_WRITE && len == 0) + return i2c_quirk_error(adap, &msgs[i], "no zero length"); + } + } + + return 0; +} + +/** + * __i2c_transfer - unlocked flavor of i2c_transfer + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * takes place. adap->algo->master_xfer existence isn't checked. + */ +int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + unsigned long orig_jiffies; + int ret, try; + + if (WARN_ON(!msgs || num < 1)) + return -EINVAL; + + ret = __i2c_check_suspended(adap); + if (ret) + return ret; + + if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) + return -EOPNOTSUPP; + + /* + * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets + * enabled. This is an efficient way of keeping the for-loop from + * being executed when not needed. + */ + if (static_branch_unlikely(&i2c_trace_msg_key)) { + int i; + for (i = 0; i < num; i++) + if (msgs[i].flags & I2C_M_RD) + trace_i2c_read(adap, &msgs[i], i); + else + trace_i2c_write(adap, &msgs[i], i); + } + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic) + ret = adap->algo->master_xfer_atomic(adap, msgs, num); + else + ret = adap->algo->master_xfer(adap, msgs, num); + + if (ret != -EAGAIN) + break; + if (time_after(jiffies, orig_jiffies + adap->timeout)) + break; + } + + if (static_branch_unlikely(&i2c_trace_msg_key)) { + int i; + for (i = 0; i < ret; i++) + if (msgs[i].flags & I2C_M_RD) + trace_i2c_reply(adap, &msgs[i], i); + trace_i2c_result(adap, num, ret); + } + + return ret; +} +EXPORT_SYMBOL(__i2c_transfer); + +/** + * i2c_transfer - execute a single or combined I2C message + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Note that there is no requirement that each message be sent to + * the same slave address, although that is the most common model. + */ +int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + int ret; + + if (!adap->algo->master_xfer) { + dev_dbg(&adap->dev, "I2C level transfers not supported\n"); + return -EOPNOTSUPP; + } + + /* REVISIT the fault reporting model here is weak: + * + * - When we get an error after receiving N bytes from a slave, + * there is no way to report "N". + * + * - When we get a NAK after transmitting N bytes to a slave, + * there is no way to report "N" ... or to let the master + * continue executing the rest of this combined message, if + * that's the appropriate response. + * + * - When for example "num" is two and we successfully complete + * the first message but get an error part way through the + * second, it's unclear whether that should be reported as + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ + ret = __i2c_lock_bus_helper(adap); + if (ret) + return ret; + + ret = __i2c_transfer(adap, msgs, num); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); + + return ret; +} +EXPORT_SYMBOL(i2c_transfer); + +/** + * i2c_transfer_buffer_flags - issue a single I2C message transferring data + * to/from a buffer + * @client: Handle to slave device + * @buf: Where the data is stored + * @count: How many bytes to transfer, must be less than 64k since msg.len is u16 + * @flags: The flags to be used for the message, e.g. I2C_M_RD for reads + * + * Returns negative errno, or else the number of bytes transferred. + */ +int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, + int count, u16 flags) +{ + int ret; + struct i2c_msg msg = { + .addr = client->addr, + .flags = flags | (client->flags & I2C_M_TEN), + .len = count, + .buf = buf, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + + /* + * If everything went ok (i.e. 1 msg transferred), return #bytes + * transferred, else error code. + */ + return (ret == 1) ? count : ret; +} +EXPORT_SYMBOL(i2c_transfer_buffer_flags); + +/** + * i2c_get_device_id - get manufacturer, part id and die revision of a device + * @client: The device to query + * @id: The queried information + * + * Returns negative errno on error, zero on success. + */ +int i2c_get_device_id(const struct i2c_client *client, + struct i2c_device_identity *id) +{ + struct i2c_adapter *adap = client->adapter; + union i2c_smbus_data raw_id; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + raw_id.block[0] = 3; + ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0, + I2C_SMBUS_READ, client->addr << 1, + I2C_SMBUS_I2C_BLOCK_DATA, &raw_id); + if (ret) + return ret; + + id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4); + id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3); + id->die_revision = raw_id.block[3] & 0x7; + return 0; +} +EXPORT_SYMBOL_GPL(i2c_get_device_id); + +/* ---------------------------------------------------- + * the i2c address scanning function + * Will not work for 10-bit addresses! + * ---------------------------------------------------- + */ + +/* + * Legacy default probe function, mostly relevant for SMBus. The default + * probe method is a quick write, but it is known to corrupt the 24RF08 + * EEPROMs due to a state machine bug, and could also irreversibly + * write-protect some EEPROMs, so for address ranges 0x30-0x37 and 0x50-0x5f, + * we use a short byte read instead. Also, some bus drivers don't implement + * quick write, so we fallback to a byte read in that case too. + * On x86, there is another special case for FSC hardware monitoring chips, + * which want regular byte reads (address 0x73.) Fortunately, these are the + * only known chips using this I2C address on PC hardware. + * Returns 1 if probe succeeded, 0 if not. + */ +static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr) +{ + int err; + union i2c_smbus_data dummy; + +#ifdef CONFIG_X86 + if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE_DATA, &dummy); + else +#endif + if (!((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50) + && i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0, + I2C_SMBUS_QUICK, NULL); + else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) + err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &dummy); + else { + dev_warn(&adap->dev, "No suitable probing method supported for address 0x%02X\n", + addr); + err = -EOPNOTSUPP; + } + + return err >= 0; +} + +static int i2c_detect_address(struct i2c_client *temp_client, + struct i2c_driver *driver) +{ + struct i2c_board_info info; + struct i2c_adapter *adapter = temp_client->adapter; + int addr = temp_client->addr; + int err; + + /* Make sure the address is valid */ + err = i2c_check_7bit_addr_validity_strict(addr); + if (err) { + dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", + addr); + return err; + } + + /* Skip if already in use (7 bit, no need to encode flags) */ + if (i2c_check_addr_busy(adapter, addr)) + return 0; + + /* Make sure there is something at this address */ + if (!i2c_default_probe(adapter, addr)) + return 0; + + /* Finally call the custom detection function */ + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = addr; + err = driver->detect(temp_client, &info); + if (err) { + /* -ENODEV is returned if the detection fails. We catch it + here as this isn't an error. */ + return err == -ENODEV ? 0 : err; + } + + /* Consistency check */ + if (info.type[0] == '\0') { + dev_err(&adapter->dev, + "%s detection function provided no name for 0x%x\n", + driver->driver.name, addr); + } else { + struct i2c_client *client; + + /* Detection succeeded, instantiate the device */ + if (adapter->class & I2C_CLASS_DEPRECATED) + dev_warn(&adapter->dev, + "This adapter will soon drop class based instantiation of devices. " + "Please make sure client 0x%02x gets instantiated by other means. " + "Check 'Documentation/i2c/instantiating-devices.rst' for details.\n", + info.addr); + + dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", + info.type, info.addr); + client = i2c_new_device(adapter, &info); + if (client) + list_add_tail(&client->detected, &driver->clients); + else + dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", + info.type, info.addr); + } + return 0; +} + +static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) +{ + const unsigned short *address_list; + struct i2c_client *temp_client; + int i, err = 0; + int adap_id = i2c_adapter_id(adapter); + + address_list = driver->address_list; + if (!driver->detect || !address_list) + return 0; + + /* Warn that the adapter lost class based instantiation */ + if (adapter->class == I2C_CLASS_DEPRECATED) { + dev_dbg(&adapter->dev, + "This adapter dropped support for I2C classes and won't auto-detect %s devices anymore. " + "If you need it, check 'Documentation/i2c/instantiating-devices.rst' for alternatives.\n", + driver->driver.name); + return 0; + } + + /* Stop here if the classes do not match */ + if (!(adapter->class & driver->class)) + return 0; + + /* Set up a temporary client to help detect callback */ + temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!temp_client) + return -ENOMEM; + temp_client->adapter = adapter; + + for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { + dev_dbg(&adapter->dev, + "found normal entry for adapter %d, addr 0x%02x\n", + adap_id, address_list[i]); + temp_client->addr = address_list[i]; + err = i2c_detect_address(temp_client, driver); + if (unlikely(err)) + break; + } + + kfree(temp_client); + return err; +} + +int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) +{ + return i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, + I2C_SMBUS_QUICK, NULL) >= 0; +} +EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); + +struct i2c_client * +i2c_new_probed_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + unsigned short const *addr_list, + int (*probe)(struct i2c_adapter *adap, unsigned short addr)) +{ + int i; + + if (!probe) + probe = i2c_default_probe; + + for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { + /* Check address validity */ + if (i2c_check_7bit_addr_validity_strict(addr_list[i]) < 0) { + dev_warn(&adap->dev, "Invalid 7-bit address 0x%02x\n", + addr_list[i]); + continue; + } + + /* Check address availability (7 bit, no need to encode flags) */ + if (i2c_check_addr_busy(adap, addr_list[i])) { + dev_dbg(&adap->dev, + "Address 0x%02x already in use, not probing\n", + addr_list[i]); + continue; + } + + /* Test address responsiveness */ + if (probe(adap, addr_list[i])) + break; + } + + if (addr_list[i] == I2C_CLIENT_END) { + dev_dbg(&adap->dev, "Probing failed, no device found\n"); + return NULL; + } + + info->addr = addr_list[i]; + return i2c_new_device(adap, info); +} +EXPORT_SYMBOL_GPL(i2c_new_probed_device); + +struct i2c_adapter *i2c_get_adapter(int nr) +{ + struct i2c_adapter *adapter; + + mutex_lock(&core_lock); + adapter = idr_find(&i2c_adapter_idr, nr); + if (!adapter) + goto exit; + + if (try_module_get(adapter->owner)) + get_device(&adapter->dev); + else + adapter = NULL; + + exit: + mutex_unlock(&core_lock); + return adapter; +} +EXPORT_SYMBOL(i2c_get_adapter); + +void i2c_put_adapter(struct i2c_adapter *adap) +{ + if (!adap) + return; + + put_device(&adap->dev); + module_put(adap->owner); +} +EXPORT_SYMBOL(i2c_put_adapter); + +/** + * i2c_get_dma_safe_msg_buf() - get a DMA safe buffer for the given i2c_msg + * @msg: the message to be checked + * @threshold: the minimum number of bytes for which using DMA makes sense. + * Should at least be 1. + * + * Return: NULL if a DMA safe buffer was not obtained. Use msg->buf with PIO. + * Or a valid pointer to be used with DMA. After use, release it by + * calling i2c_put_dma_safe_msg_buf(). + * + * This function must only be called from process context! + */ +u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold) +{ + /* also skip 0-length msgs for bogus thresholds of 0 */ + if (!threshold) + pr_debug("DMA buffer for addr=0x%02x with length 0 is bogus\n", + msg->addr); + if (msg->len < threshold || msg->len == 0) + return NULL; + + if (msg->flags & I2C_M_DMA_SAFE) + return msg->buf; + + pr_debug("using bounce buffer for addr=0x%02x, len=%d\n", + msg->addr, msg->len); + + if (msg->flags & I2C_M_RD) + return kzalloc(msg->len, GFP_KERNEL); + else + return kmemdup(msg->buf, msg->len, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(i2c_get_dma_safe_msg_buf); + +/** + * i2c_put_dma_safe_msg_buf - release DMA safe buffer and sync with i2c_msg + * @buf: the buffer obtained from i2c_get_dma_safe_msg_buf(). May be NULL. + * @msg: the message which the buffer corresponds to + * @xferred: bool saying if the message was transferred + */ +void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred) +{ + if (!buf || buf == msg->buf) + return; + + if (xferred && msg->flags & I2C_M_RD) + memcpy(msg->buf, buf, msg->len); + + kfree(buf); +} +EXPORT_SYMBOL_GPL(i2c_put_dma_safe_msg_buf); + +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_LICENSE("GPL"); diff --git a/t/tree/drivers/i2c/i2c-core-of.c b/t/tree/drivers/i2c/i2c-core-of.c new file mode 100644 index 00000000..7eb41990 --- /dev/null +++ b/t/tree/drivers/i2c/i2c-core-of.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core OF support code + * + * Copyright (C) 2008 Jochen Friedrich + * based on a previous patch from Jon Smirl + * + * Copyright (C) 2013, 2018 Wolfram Sang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +int of_i2c_get_board_info(struct device *dev, struct device_node *node, + struct i2c_board_info *info) +{ + u32 addr; + int ret; + + memset(info, 0, sizeof(*info)); + + if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) { + dev_err(dev, "of_i2c: modalias failure on %pOF\n", node); + return -EINVAL; + } + + ret = of_property_read_u32(node, "reg", &addr); + if (ret) { + dev_err(dev, "of_i2c: invalid reg on %pOF\n", node); + return ret; + } + + if (addr & I2C_TEN_BIT_ADDRESS) { + addr &= ~I2C_TEN_BIT_ADDRESS; + info->flags |= I2C_CLIENT_TEN; + } + + if (addr & I2C_OWN_SLAVE_ADDRESS) { + addr &= ~I2C_OWN_SLAVE_ADDRESS; + info->flags |= I2C_CLIENT_SLAVE; + } + + info->addr = addr; + info->of_node = node; + + if (of_property_read_bool(node, "host-notify")) + info->flags |= I2C_CLIENT_HOST_NOTIFY; + + if (of_get_property(node, "wakeup-source", NULL)) + info->flags |= I2C_CLIENT_WAKE; + + return 0; +} +EXPORT_SYMBOL_GPL(of_i2c_get_board_info); + +static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, + struct device_node *node) +{ + struct i2c_client *client; + struct i2c_board_info info; + int ret; + + dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node); + + ret = of_i2c_get_board_info(&adap->dev, node, &info); + if (ret) + return ERR_PTR(ret); + + client = i2c_new_device(adap, &info); + if (!client) { + dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node); + return ERR_PTR(-EINVAL); + } + return client; +} + +void of_i2c_register_devices(struct i2c_adapter *adap) +{ + struct device_node *bus, *node; + struct i2c_client *client; + + /* Only register child devices if the adapter has a node pointer set */ + if (!adap->dev.of_node) + return; + + dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); + + bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus"); + if (!bus) + bus = of_node_get(adap->dev.of_node); + + for_each_available_child_of_node(bus, node) { + if (of_node_test_and_set_flag(node, OF_POPULATED)) + continue; + + client = of_i2c_register_device(adap, node); + if (IS_ERR(client)) { + dev_err(&adap->dev, + "Failed to create I2C device for %pOF\n", + node); + of_node_clear_flag(node, OF_POPULATED); + } + } + + of_node_put(bus); +} + +static int of_dev_or_parent_node_match(struct device *dev, const void *data) +{ + if (dev->of_node == data) + return 1; + + if (dev->parent) + return dev->parent->of_node == data; + + return 0; +} + +/* must call put_device() when done with returned i2c_client device */ +struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) +{ + struct device *dev; + struct i2c_client *client; + + dev = bus_find_device_by_of_node(&i2c_bus_type, node); + if (!dev) + return NULL; + + client = i2c_verify_client(dev); + if (!client) + put_device(dev); + + return client; +} +EXPORT_SYMBOL(of_find_i2c_device_by_node); + +/* must call put_device() when done with returned i2c_adapter device */ +struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) +{ + struct device *dev; + struct i2c_adapter *adapter; + + dev = bus_find_device(&i2c_bus_type, NULL, node, + of_dev_or_parent_node_match); + if (!dev) + return NULL; + + adapter = i2c_verify_adapter(dev); + if (!adapter) + put_device(dev); + + return adapter; +} +EXPORT_SYMBOL(of_find_i2c_adapter_by_node); + +/* must call i2c_put_adapter() when done with returned i2c_adapter device */ +struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) +{ + struct i2c_adapter *adapter; + + adapter = of_find_i2c_adapter_by_node(node); + if (!adapter) + return NULL; + + if (!try_module_get(adapter->owner)) { + put_device(&adapter->dev); + adapter = NULL; + } + + return adapter; +} +EXPORT_SYMBOL(of_get_i2c_adapter_by_node); + +static const struct of_device_id* +i2c_of_match_device_sysfs(const struct of_device_id *matches, + struct i2c_client *client) +{ + const char *name; + + for (; matches->compatible[0]; matches++) { + /* + * Adding devices through the i2c sysfs interface provides us + * a string to match which may be compatible with the device + * tree compatible strings, however with no actual of_node the + * of_match_device() will not match + */ + if (sysfs_streq(client->name, matches->compatible)) + return matches; + + name = strchr(matches->compatible, ','); + if (!name) + name = matches->compatible; + else + name++; + + if (sysfs_streq(client->name, name)) + return matches; + } + + return NULL; +} + +const struct of_device_id +*i2c_of_match_device(const struct of_device_id *matches, + struct i2c_client *client) +{ + const struct of_device_id *match; + + if (!(client && matches)) + return NULL; + + match = of_match_device(matches, &client->dev); + if (match) + return match; + + return i2c_of_match_device_sysfs(matches, client); +} +EXPORT_SYMBOL_GPL(i2c_of_match_device); + +#if IS_ENABLED(CONFIG_OF_DYNAMIC) +static int of_i2c_notify(struct notifier_block *nb, unsigned long action, + void *arg) +{ + struct of_reconfig_data *rd = arg; + struct i2c_adapter *adap; + struct i2c_client *client; + + switch (of_reconfig_get_state_change(action, rd)) { + case OF_RECONFIG_CHANGE_ADD: + adap = of_find_i2c_adapter_by_node(rd->dn->parent); + if (adap == NULL) + return NOTIFY_OK; /* not for us */ + + if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) { + put_device(&adap->dev); + return NOTIFY_OK; + } + + client = of_i2c_register_device(adap, rd->dn); + if (IS_ERR(client)) { + dev_err(&adap->dev, "failed to create client for '%pOF'\n", + rd->dn); + put_device(&adap->dev); + of_node_clear_flag(rd->dn, OF_POPULATED); + return notifier_from_errno(PTR_ERR(client)); + } + put_device(&adap->dev); + break; + case OF_RECONFIG_CHANGE_REMOVE: + /* already depopulated? */ + if (!of_node_check_flag(rd->dn, OF_POPULATED)) + return NOTIFY_OK; + + /* find our device by node */ + client = of_find_i2c_device_by_node(rd->dn); + if (client == NULL) + return NOTIFY_OK; /* no? not meant for us */ + + /* unregister takes one ref away */ + i2c_unregister_device(client); + + /* and put the reference of the find */ + put_device(&client->dev); + break; + } + + return NOTIFY_OK; +} + +struct notifier_block i2c_of_notifier = { + .notifier_call = of_i2c_notify, +}; +#endif /* CONFIG_OF_DYNAMIC */ diff --git a/t/tree/drivers/i2c/i2c-core-slave.c b/t/tree/drivers/i2c/i2c-core-slave.c new file mode 100644 index 00000000..5427f047 --- /dev/null +++ b/t/tree/drivers/i2c/i2c-core-slave.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core slave support code + * + * Copyright (C) 2014 by Wolfram Sang + */ + +#include +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) +{ + int ret; + + if (!client || !slave_cb) { + WARN(1, "insufficient data\n"); + return -EINVAL; + } + + if (!(client->flags & I2C_CLIENT_SLAVE)) + dev_warn(&client->dev, "%s: client slave flag not set. You might see address collisions\n", + __func__); + + if (!(client->flags & I2C_CLIENT_TEN)) { + /* Enforce stricter address checking */ + ret = i2c_check_7bit_addr_validity_strict(client->addr); + if (ret) { + dev_err(&client->dev, "%s: invalid address\n", __func__); + return ret; + } + } + + if (!client->adapter->algo->reg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); + return -EOPNOTSUPP; + } + + client->slave_cb = slave_cb; + + i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + ret = client->adapter->algo->reg_slave(client); + i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + + if (ret) { + client->slave_cb = NULL; + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_register); + +int i2c_slave_unregister(struct i2c_client *client) +{ + int ret; + + if (!client->adapter->algo->unreg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); + return -EOPNOTSUPP; + } + + i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + ret = client->adapter->algo->unreg_slave(client); + i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER); + + if (ret == 0) + client->slave_cb = NULL; + else + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_unregister); + +/** + * i2c_detect_slave_mode - detect operation mode + * @dev: The device owning the bus + * + * This checks the device nodes for an I2C slave by checking the address + * used in the reg property. If the address match the I2C_OWN_SLAVE_ADDRESS + * flag this means the device is configured to act as a I2C slave and it will + * be listening at that address. + * + * Returns true if an I2C own slave address is detected, otherwise returns + * false. + */ +bool i2c_detect_slave_mode(struct device *dev) +{ + if (IS_BUILTIN(CONFIG_OF) && dev->of_node) { + struct device_node *child; + u32 reg; + + for_each_child_of_node(dev->of_node, child) { + of_property_read_u32(child, "reg", ®); + if (reg & I2C_OWN_SLAVE_ADDRESS) { + of_node_put(child); + return true; + } + } + } else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) { + dev_dbg(dev, "ACPI slave is not supported yet\n"); + } + return false; +} +EXPORT_SYMBOL_GPL(i2c_detect_slave_mode); diff --git a/t/tree/drivers/i2c/i2c-core-smbus.c b/t/tree/drivers/i2c/i2c-core-smbus.c new file mode 100644 index 00000000..3ac426a8 --- /dev/null +++ b/t/tree/drivers/i2c/i2c-core-smbus.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux I2C core SMBus and SMBus emulation code + * + * This file contains the SMBus functions which are always included in the I2C + * core because they can be emulated via I2C. SMBus specific extensions + * (e.g. smbalert) are handled in a seperate i2c-smbus module. + * + * All SMBus-related things are written by Frodo Looijaard + * SMBus 2.0 support by Mark Studebaker and + * Jean Delvare + */ +#include +#include +#include +#include +#include + +#include "i2c-core.h" + +#define CREATE_TRACE_POINTS +#include + + +/* The SMBus parts */ + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/* Incremental CRC8 over count bytes in the array pointed to by p */ +static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) +{ + int i; + + for (i = 0; i < count; i++) + crc = crc8((crc ^ p[i]) << 8); + return crc; +} + +/* Assume a 7-bit address, which is reasonable for SMBus */ +static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) +{ + /* The address will be sent first */ + u8 addr = i2c_8bit_addr_from_msg(msg); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* Used for write only transactions */ +static inline void i2c_smbus_add_pec(struct i2c_msg *msg) +{ + msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg); + msg->len++; +} + +/* Return <0 on CRC error + If there was a write before this read (most cases) we need to take the + partial CRC from the write part into account. + Note that this function does modify the message (we need to decrease the + message length to hide the CRC byte from the caller). */ +static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg) +{ + u8 rpec = msg->buf[--msg->len]; + cpec = i2c_smbus_msg_pec(cpec, msg); + + if (rpec != cpec) { + pr_debug("Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec); + return -EBADMSG; + } + return 0; +} + +/** + * i2c_smbus_read_byte - SMBus "receive byte" protocol + * @client: Handle to slave device + * + * This executes the SMBus "receive byte" protocol, returning negative errno + * else the byte received from the device. + */ +s32 i2c_smbus_read_byte(const struct i2c_client *client) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, 0, + I2C_SMBUS_BYTE, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte); + +/** + * i2c_smbus_write_byte - SMBus "send byte" protocol + * @client: Handle to slave device + * @value: Byte to be sent + * + * This executes the SMBus "send byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} +EXPORT_SYMBOL(i2c_smbus_write_byte); + +/** + * i2c_smbus_read_byte_data - SMBus "read byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read byte" protocol, returning negative errno + * else a data byte received from the device. + */ +s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data); + return (status < 0) ? status : data.byte; +} +EXPORT_SYMBOL(i2c_smbus_read_byte_data); + +/** + * i2c_smbus_write_byte_data - SMBus "write byte" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: Byte being written + * + * This executes the SMBus "write byte" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_byte_data); + +/** + * i2c_smbus_read_word_data - SMBus "read word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * + * This executes the SMBus "read word" protocol, returning negative errno + * else a 16-bit unsigned "word" received from the device. + */ +s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data); + return (status < 0) ? status : data.word; +} +EXPORT_SYMBOL(i2c_smbus_read_word_data); + +/** + * i2c_smbus_write_word_data - SMBus "write word" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @value: 16-bit "word" being written + * + * This executes the SMBus "write word" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_word_data); + +/** + * i2c_smbus_read_block_data - SMBus "block read" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most 32 bytes. + * + * This executes the SMBus "block read" protocol, returning negative errno + * else the number of data bytes in the slave's response. + * + * Note that using this function requires that the client's adapter support + * the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers + * support this; its emulation through I2C messaging relies on a specific + * mechanism (I2C_M_RECV_LEN) which may not be implemented. + */ +s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, + u8 *values) +{ + union i2c_smbus_data data; + int status; + + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BLOCK_DATA, &data); + if (status) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_block_data); + +/** + * i2c_smbus_write_block_data - SMBus "block write" protocol + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most 32 bytes + * @values: Byte array which will be written. + * + * This executes the SMBus "block write" protocol, returning negative errno + * else zero on success. + */ +s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +/* Returns the number of read bytes */ +s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + status = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (status < 0) + return status; + + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); + +s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); + +static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val) +{ + bool is_read = msg->flags & I2C_M_RD; + unsigned char *dma_buf; + + dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL); + if (!dma_buf) + return; + + msg->buf = dma_buf; + msg->flags |= I2C_M_DMA_SAFE; + + if (init_val) + msg->buf[0] = init_val; +} + +/* + * Simulate a SMBus command using the I2C protocol. + * No checking of parameters is done! + */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + /* + * So we need to generate a series of msgs. In the case of writing, we + * need to use only one message; when reading, we need two. We + * initialize most things with sane defaults, to keep the code below + * somewhat simpler. + */ + unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + int num = read_write == I2C_SMBUS_READ ? 2 : 1; + int i; + u8 partial_pec = 0; + int status; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = flags, + .len = 1, + .buf = msgbuf0, + }, { + .addr = addr, + .flags = flags | I2C_M_RD, + .len = 0, + .buf = msgbuf1, + }, + }; + + msgbuf0[0] = command; + switch (size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write == I2C_SMBUS_READ ? + I2C_M_RD : 0); + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len = 3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + read_write = I2C_SMBUS_READ; + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = data->word >> 8; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + i2c_smbus_try_get_dmabuf(&msg[1], 0); + } else { + msg[0].len = data->block[0] + 2; + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + + i2c_smbus_try_get_dmabuf(&msg[0], command); + for (i = 1; i < msg[0].len; i++) + msg[0].buf[i] = data->block[i - 1]; + } + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + num = 2; /* Another special case */ + read_write = I2C_SMBUS_READ; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, + "Invalid block write size %d\n", + data->block[0]); + return -EINVAL; + } + + msg[0].len = data->block[0] + 2; + i2c_smbus_try_get_dmabuf(&msg[0], command); + for (i = 1; i < msg[0].len; i++) + msg[0].buf[i] = data->block[i - 1]; + + msg[1].flags |= I2C_M_RECV_LEN; + msg[1].len = 1; /* block length will be added by + the underlying bus driver */ + i2c_smbus_try_get_dmabuf(&msg[1], 0); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + dev_err(&adapter->dev, "Invalid block %s size %d\n", + read_write == I2C_SMBUS_READ ? "read" : "write", + data->block[0]); + return -EINVAL; + } + + if (read_write == I2C_SMBUS_READ) { + msg[1].len = data->block[0]; + i2c_smbus_try_get_dmabuf(&msg[1], 0); + } else { + msg[0].len = data->block[0] + 1; + + i2c_smbus_try_get_dmabuf(&msg[0], command); + for (i = 1; i <= data->block[0]; i++) + msg[0].buf[i] = data->block[i]; + } + break; + default: + dev_err(&adapter->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK + && size != I2C_SMBUS_I2C_BLOCK_DATA); + if (i) { + /* Compute PEC if first message is a write */ + if (!(msg[0].flags & I2C_M_RD)) { + if (num == 1) /* Write only */ + i2c_smbus_add_pec(&msg[0]); + else /* Write followed by read */ + partial_pec = i2c_smbus_msg_pec(0, &msg[0]); + } + /* Ask for PEC if last message is a read */ + if (msg[num-1].flags & I2C_M_RD) + msg[num-1].len++; + } + + status = __i2c_transfer(adapter, msg, num); + if (status < 0) + goto cleanup; + if (status != num) { + status = -EIO; + goto cleanup; + } + status = 0; + + /* Check PEC if last message is a read */ + if (i && (msg[num-1].flags & I2C_M_RD)) { + status = i2c_smbus_check_pec(partial_pec, &msg[num-1]); + if (status < 0) + goto cleanup; + } + + if (read_write == I2C_SMBUS_READ) + switch (size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < data->block[0]; i++) + data->block[i + 1] = msg[1].buf[i]; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + for (i = 0; i < msg[1].buf[0] + 1; i++) + data->block[i] = msg[1].buf[i]; + break; + } + +cleanup: + if (msg[0].flags & I2C_M_DMA_SAFE) + kfree(msg[0].buf); + if (msg[1].flags & I2C_M_DMA_SAFE) + kfree(msg[1].buf); + + return status; +} + +/** + * i2c_smbus_xfer - execute SMBus protocol operations + * @adapter: Handle to I2C bus + * @addr: Address of SMBus slave on that bus + * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) + * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE + * @command: Byte interpreted by slave, for protocols which use such bytes + * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL + * @data: Data to be read or written + * + * This executes an SMBus protocol operation, and returns a negative + * errno code else zero on success. + */ +s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) +{ + s32 res; + + res = __i2c_lock_bus_helper(adapter); + if (res) + return res; + + res = __i2c_smbus_xfer(adapter, addr, flags, read_write, + command, protocol, data); + i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); + + return res; +} +EXPORT_SYMBOL(i2c_smbus_xfer); + +s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int protocol, union i2c_smbus_data *data) +{ + int (*xfer_func)(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); + unsigned long orig_jiffies; + int try; + s32 res; + + res = __i2c_check_suspended(adapter); + if (res) + return res; + + /* If enabled, the following two tracepoints are conditional on + * read_write and protocol. + */ + trace_smbus_write(adapter, addr, flags, read_write, + command, protocol, data); + trace_smbus_read(adapter, addr, flags, read_write, + command, protocol); + + flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; + + xfer_func = adapter->algo->smbus_xfer; + if (i2c_in_atomic_xfer_mode()) { + if (adapter->algo->smbus_xfer_atomic) + xfer_func = adapter->algo->smbus_xfer_atomic; + else if (adapter->algo->master_xfer_atomic) + xfer_func = NULL; /* fallback to I2C emulation */ + } + + if (xfer_func) { + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (res = 0, try = 0; try <= adapter->retries; try++) { + res = xfer_func(adapter, addr, flags, read_write, + command, protocol, data); + if (res != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + adapter->timeout)) + break; + } + + if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) + goto trace; + /* + * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't + * implement native support for the SMBus operation. + */ + } + + res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); + +trace: + /* If enabled, the reply tracepoint is conditional on read_write. */ + trace_smbus_reply(adapter, addr, flags, read_write, + command, protocol, data, res); + trace_smbus_result(adapter, addr, flags, read_write, + command, protocol, res); + + return res; +} +EXPORT_SYMBOL(__i2c_smbus_xfer); + +/** + * i2c_smbus_read_i2c_block_data_or_emulated - read block or emulate + * @client: Handle to slave device + * @command: Byte interpreted by slave + * @length: Size of data block; SMBus allows at most I2C_SMBUS_BLOCK_MAX bytes + * @values: Byte array into which data will be read; big enough to hold + * the data returned by the slave. SMBus allows at most + * I2C_SMBUS_BLOCK_MAX bytes. + * + * This executes the SMBus "block read" protocol if supported by the adapter. + * If block read is not supported, it emulates it using either word or byte + * read protocols depending on availability. + * + * The addresses of the I2C slave device that are accessed with this function + * must be mapped to a linear region, so that a block read will have the same + * effect as a byte read. Before using this function you must double-check + * if the I2C slave does support exchanging a block transfer with a byte + * transfer. + */ +s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, + u8 command, u8 length, u8 *values) +{ + u8 i = 0; + int status; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return i2c_smbus_read_i2c_block_data(client, command, length, values); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -EOPNOTSUPP; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) { + while ((i + 2) <= length) { + status = i2c_smbus_read_word_data(client, command + i); + if (status < 0) + return status; + values[i] = status & 0xff; + values[i + 1] = status >> 8; + i += 2; + } + } + + while (i < length) { + status = i2c_smbus_read_byte_data(client, command + i); + if (status < 0) + return status; + values[i] = status; + i++; + } + + return i; +} +EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); + +/** + * i2c_setup_smbus_alert - Setup SMBus alert support + * @adapter: the target adapter + * @setup: setup data for the SMBus alert handler + * Context: can sleep + * + * Setup handling of the SMBus alert protocol on a given I2C bus segment. + * + * Handling can be done either through our IRQ handler, or by the + * adapter (from its handler, periodic polling, or whatever). + * + * NOTE that if we manage the IRQ, we *MUST* know if it's level or + * edge triggered in order to hand it to the workqueue correctly. + * If triggering the alert seems to wedge the system, you probably + * should have said it's level triggered. + * + * This returns the ara client, which should be saved for later use with + * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL + * to indicate an error. + */ +struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, + struct i2c_smbus_alert_setup *setup) +{ + struct i2c_board_info ara_board_info = { + I2C_BOARD_INFO("smbus_alert", 0x0c), + .platform_data = setup, + }; + + return i2c_new_device(adapter, &ara_board_info); +} +EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); + +#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) +int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) +{ + struct i2c_client *client; + int irq; + + irq = of_property_match_string(adapter->dev.of_node, "interrupt-names", + "smbus_alert"); + if (irq == -EINVAL || irq == -ENODATA) + return 0; + else if (irq < 0) + return irq; + + client = i2c_setup_smbus_alert(adapter, NULL); + if (!client) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert); +#endif diff --git a/t/tree/drivers/i2c/i2c-core.h b/t/tree/drivers/i2c/i2c-core.h new file mode 100644 index 00000000..517d98be --- /dev/null +++ b/t/tree/drivers/i2c/i2c-core.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * i2c-core.h - interfaces internal to the I2C framework + */ + +#include + +struct i2c_devinfo { + struct list_head list; + int busnum; + struct i2c_board_info board_info; +}; + +/* board_lock protects board_list and first_dynamic_bus_num. + * only i2c core components are allowed to use these symbols. + */ +extern struct rw_semaphore __i2c_board_lock; +extern struct list_head __i2c_board_list; +extern int __i2c_first_dynamic_bus_num; + +int i2c_check_7bit_addr_validity_strict(unsigned short addr); +int i2c_dev_irq_from_resources(const struct resource *resources, + unsigned int num_resources); + +/* + * We only allow atomic transfers for very late communication, e.g. to send + * the powerdown command to a PMIC. Atomic transfers are a corner case and not + * for generic use! + */ +static inline bool i2c_in_atomic_xfer_mode(void) +{ + return system_state > SYSTEM_RUNNING && irqs_disabled(); +} + +static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap) +{ + int ret = 0; + + if (i2c_in_atomic_xfer_mode()) { + WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic, + "No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev)); + ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN; + } else { + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); + } + + return ret; +} + +static inline int __i2c_check_suspended(struct i2c_adapter *adap) +{ + if (test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)) { + if (!test_and_set_bit(I2C_ALF_SUSPEND_REPORTED, &adap->locked_flags)) + dev_WARN(&adap->dev, "Transfer while suspended\n"); + return -ESHUTDOWN; + } + + return 0; +} + +#ifdef CONFIG_ACPI +const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client); +void i2c_acpi_register_devices(struct i2c_adapter *adap); + +int i2c_acpi_get_irq(struct i2c_client *client); +#else /* CONFIG_ACPI */ +static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } +static inline const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client) +{ + return NULL; +} + +static inline int i2c_acpi_get_irq(struct i2c_client *client) +{ + return 0; +} +#endif /* CONFIG_ACPI */ +extern struct notifier_block i2c_acpi_notifier; + +#ifdef CONFIG_ACPI_I2C_OPREGION +int i2c_acpi_install_space_handler(struct i2c_adapter *adapter); +void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter); +#else /* CONFIG_ACPI_I2C_OPREGION */ +static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { return 0; } +static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { } +#endif /* CONFIG_ACPI_I2C_OPREGION */ + +#ifdef CONFIG_OF +void of_i2c_register_devices(struct i2c_adapter *adap); +#else +static inline void of_i2c_register_devices(struct i2c_adapter *adap) { } +#endif +extern struct notifier_block i2c_of_notifier; diff --git a/t/tree/drivers/i2c/i2c-dev.c b/t/tree/drivers/i2c/i2c-dev.c new file mode 100644 index 00000000..2ea4585d --- /dev/null +++ b/t/tree/drivers/i2c/i2c-dev.c @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + Copyright (C) 2003 Greg Kroah-Hartman + +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +/* The I2C_RDWR ioctl code is written by Kolja Waschk */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a + * slave (i2c_client) with which messages will be exchanged. It's coupled + * with a character special file which is accessed by user mode drivers. + * + * The list of i2c_dev structures is parallel to the i2c_adapter lists + * maintained by the driver model, and is updated using bus notifications. + */ +struct i2c_dev { + struct list_head list; + struct i2c_adapter *adap; + struct device *dev; + struct cdev cdev; +}; + +#define I2C_MINORS (MINORMASK + 1) +static LIST_HEAD(i2c_dev_list); +static DEFINE_SPINLOCK(i2c_dev_list_lock); + +static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) +{ + struct i2c_dev *i2c_dev; + + spin_lock(&i2c_dev_list_lock); + list_for_each_entry(i2c_dev, &i2c_dev_list, list) { + if (i2c_dev->adap->nr == index) + goto found; + } + i2c_dev = NULL; +found: + spin_unlock(&i2c_dev_list_lock); + return i2c_dev; +} + +static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) +{ + struct i2c_dev *i2c_dev; + + if (adap->nr >= I2C_MINORS) { + printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n", + adap->nr); + return ERR_PTR(-ENODEV); + } + + i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) + return ERR_PTR(-ENOMEM); + i2c_dev->adap = adap; + + spin_lock(&i2c_dev_list_lock); + list_add_tail(&i2c_dev->list, &i2c_dev_list); + spin_unlock(&i2c_dev_list_lock); + return i2c_dev; +} + +static void put_i2c_dev(struct i2c_dev *i2c_dev) +{ + spin_lock(&i2c_dev_list_lock); + list_del(&i2c_dev->list); + spin_unlock(&i2c_dev_list_lock); + kfree(i2c_dev); +} + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt)); + + if (!i2c_dev) + return -ENODEV; + return sprintf(buf, "%s\n", i2c_dev->adap->name); +} +static DEVICE_ATTR_RO(name); + +static struct attribute *i2c_attrs[] = { + &dev_attr_name.attr, + NULL, +}; +ATTRIBUTE_GROUPS(i2c); + +/* ------------------------------------------------------------------------- */ + +/* + * After opening an instance of this character special file, a file + * descriptor starts out associated only with an i2c_adapter (and bus). + * + * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg + * traffic to any devices on the bus used by that adapter. That's because + * the i2c_msg vectors embed all the addressing information they need, and + * are submitted directly to an i2c_adapter. However, SMBus-only adapters + * don't support that interface. + * + * To use read()/write() system calls on that file descriptor, or to use + * SMBus interfaces (and work with SMBus-only hosts!), you must first issue + * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous + * (never registered) i2c_client so it holds the addressing information + * needed by those system calls and by this SMBus interface. + */ + +static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, + loff_t *offset) +{ + char *tmp; + int ret; + + struct i2c_client *client = file->private_data; + + if (count > 8192) + count = 8192; + + tmp = kmalloc(count, GFP_KERNEL); + if (tmp == NULL) + return -ENOMEM; + + pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", + iminor(file_inode(file)), count); + + ret = i2c_master_recv(client, tmp, count); + if (ret >= 0) + ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; + kfree(tmp); + return ret; +} + +static ssize_t i2cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + char *tmp; + struct i2c_client *client = file->private_data; + + if (count > 8192) + count = 8192; + + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", + iminor(file_inode(file)), count); + + ret = i2c_master_send(client, tmp, count); + kfree(tmp); + return ret; +} + +static int i2cdev_check(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + + if (!client || client->addr != *(unsigned int *)addrp) + return 0; + + return dev->driver ? -EBUSY : 0; +} + +/* walk up mux tree */ +static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result; + + result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); + if (!result && parent) + result = i2cdev_check_mux_parents(parent, addr); + + return result; +} + +/* recurse down mux tree */ +static int i2cdev_check_mux_children(struct device *dev, void *addrp) +{ + int result; + + if (dev->type == &i2c_adapter_type) + result = device_for_each_child(dev, addrp, + i2cdev_check_mux_children); + else + result = i2cdev_check(dev, addrp); + + return result; +} + +/* This address checking function differs from the one in i2c-core + in that it considers an address with a registered device, but no + driver bound to it, as NOT busy. */ +static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) +{ + struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); + int result = 0; + + if (parent) + result = i2cdev_check_mux_parents(parent, addr); + + if (!result) + result = device_for_each_child(&adapter->dev, &addr, + i2cdev_check_mux_children); + + return result; +} + +static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, + unsigned nmsgs, struct i2c_msg *msgs) +{ + u8 __user **data_ptrs; + int i, res; + + data_ptrs = kmalloc_array(nmsgs, sizeof(u8 __user *), GFP_KERNEL); + if (data_ptrs == NULL) { + kfree(msgs); + return -ENOMEM; + } + + res = 0; + for (i = 0; i < nmsgs; i++) { + /* Limit the size of the message to a sane amount */ + if (msgs[i].len > 8192) { + res = -EINVAL; + break; + } + + data_ptrs[i] = (u8 __user *)msgs[i].buf; + msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len); + if (IS_ERR(msgs[i].buf)) { + res = PTR_ERR(msgs[i].buf); + break; + } + /* memdup_user allocates with GFP_KERNEL, so DMA is ok */ + msgs[i].flags |= I2C_M_DMA_SAFE; + + /* + * If the message length is received from the slave (similar + * to SMBus block read), we must ensure that the buffer will + * be large enough to cope with a message length of + * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus + * drivers allow. The first byte in the buffer must be + * pre-filled with the number of extra bytes, which must be + * at least one to hold the message length, but can be + * greater (for example to account for a checksum byte at + * the end of the message.) + */ + if (msgs[i].flags & I2C_M_RECV_LEN) { + if (!(msgs[i].flags & I2C_M_RD) || + msgs[i].len < 1 || msgs[i].buf[0] < 1 || + msgs[i].len < msgs[i].buf[0] + + I2C_SMBUS_BLOCK_MAX) { + i++; + res = -EINVAL; + break; + } + + msgs[i].len = msgs[i].buf[0]; + } + } + if (res < 0) { + int j; + for (j = 0; j < i; ++j) + kfree(msgs[j].buf); + kfree(data_ptrs); + kfree(msgs); + return res; + } + + res = i2c_transfer(client->adapter, msgs, nmsgs); + while (i-- > 0) { + if (res >= 0 && (msgs[i].flags & I2C_M_RD)) { + if (copy_to_user(data_ptrs[i], msgs[i].buf, + msgs[i].len)) + res = -EFAULT; + } + kfree(msgs[i].buf); + } + kfree(data_ptrs); + kfree(msgs); + return res; +} + +static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, + u8 read_write, u8 command, u32 size, + union i2c_smbus_data __user *data) +{ + union i2c_smbus_data temp = {}; + int datasize, res; + + if ((size != I2C_SMBUS_BYTE) && + (size != I2C_SMBUS_QUICK) && + (size != I2C_SMBUS_BYTE_DATA) && + (size != I2C_SMBUS_WORD_DATA) && + (size != I2C_SMBUS_PROC_CALL) && + (size != I2C_SMBUS_BLOCK_DATA) && + (size != I2C_SMBUS_I2C_BLOCK_BROKEN) && + (size != I2C_SMBUS_I2C_BLOCK_DATA) && + (size != I2C_SMBUS_BLOCK_PROC_CALL)) { + dev_dbg(&client->adapter->dev, + "size out of range (%x) in ioctl I2C_SMBUS.\n", + size); + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((read_write != I2C_SMBUS_READ) && + (read_write != I2C_SMBUS_WRITE)) { + dev_dbg(&client->adapter->dev, + "read_write out of range (%x) in ioctl I2C_SMBUS.\n", + read_write); + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((size == I2C_SMBUS_QUICK) || + ((size == I2C_SMBUS_BYTE) && + (read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, read_write, + command, size, NULL); + + if (data == NULL) { + dev_dbg(&client->adapter->dev, + "data is NULL pointer in ioctl I2C_SMBUS.\n"); + return -EINVAL; + } + + if ((size == I2C_SMBUS_BYTE_DATA) || + (size == I2C_SMBUS_BYTE)) + datasize = sizeof(data->byte); + else if ((size == I2C_SMBUS_WORD_DATA) || + (size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data->word); + else /* size == smbus block, i2c block, or block proc. call */ + datasize = sizeof(data->block); + + if ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (size == I2C_SMBUS_I2C_BLOCK_DATA) || + (read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data, datasize)) + return -EFAULT; + } + if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) { + /* Convert old I2C block commands to the new + convention. This preserves binary compatibility. */ + size = I2C_SMBUS_I2C_BLOCK_DATA; + if (read_write == I2C_SMBUS_READ) + temp.block[0] = I2C_SMBUS_BLOCK_MAX; + } + res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + read_write, command, size, &temp); + if (!res && ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data, &temp, datasize)) + return -EFAULT; + } + return res; +} + +static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + unsigned long funcs; + + dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", + cmd, arg); + + switch (cmd) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) + return -EBUSY; + /* REVISIT: address could become busy later */ + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_PEC: + /* + * Setting the PEC flag here won't affect kernel drivers, + * which will be using the i2c_client node registered with + * the driver model core. Likewise, when that client has + * the PEC flag already set, the i2c-dev driver won't see + * (or use) this setting. + */ + if (arg) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (unsigned long __user *)arg); + + case I2C_RDWR: { + struct i2c_rdwr_ioctl_data rdwr_arg; + struct i2c_msg *rdwr_pa; + + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data __user *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) + return -EINVAL; + + rdwr_pa = memdup_user(rdwr_arg.msgs, + rdwr_arg.nmsgs * sizeof(struct i2c_msg)); + if (IS_ERR(rdwr_pa)) + return PTR_ERR(rdwr_pa); + + return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); + } + + case I2C_SMBUS: { + struct i2c_smbus_ioctl_data data_arg; + if (copy_from_user(&data_arg, + (struct i2c_smbus_ioctl_data __user *) arg, + sizeof(struct i2c_smbus_ioctl_data))) + return -EFAULT; + return i2cdev_ioctl_smbus(client, data_arg.read_write, + data_arg.command, + data_arg.size, + data_arg.data); + } + case I2C_RETRIES: + if (arg > INT_MAX) + return -EINVAL; + + client->adapter->retries = arg; + break; + case I2C_TIMEOUT: + if (arg > INT_MAX) + return -EINVAL; + + /* For historical reasons, user-space sets the timeout + * value in units of 10 ms. + */ + client->adapter->timeout = msecs_to_jiffies(arg * 10); + break; + default: + /* NOTE: returning a fault code here could cause trouble + * in buggy userspace code. Some old kernel bugs returned + * zero in this case, and userspace code might accidentally + * have depended on that bug. + */ + return -ENOTTY; + } + return 0; +} + +#ifdef CONFIG_COMPAT + +struct i2c_smbus_ioctl_data32 { + u8 read_write; + u8 command; + u32 size; + compat_caddr_t data; /* union i2c_smbus_data *data */ +}; + +struct i2c_msg32 { + u16 addr; + u16 flags; + u16 len; + compat_caddr_t buf; +}; + +struct i2c_rdwr_ioctl_data32 { + compat_caddr_t msgs; /* struct i2c_msg __user *msgs */ + u32 nmsgs; +}; + +static long compat_i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = file->private_data; + unsigned long funcs; + switch (cmd) { + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (compat_ulong_t __user *)arg); + case I2C_RDWR: { + struct i2c_rdwr_ioctl_data32 rdwr_arg; + struct i2c_msg32 *p; + struct i2c_msg *rdwr_pa; + int i; + + if (copy_from_user(&rdwr_arg, + (struct i2c_rdwr_ioctl_data32 __user *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + + if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) + return -EINVAL; + + rdwr_pa = kmalloc_array(rdwr_arg.nmsgs, sizeof(struct i2c_msg), + GFP_KERNEL); + if (!rdwr_pa) + return -ENOMEM; + + p = compat_ptr(rdwr_arg.msgs); + for (i = 0; i < rdwr_arg.nmsgs; i++) { + struct i2c_msg32 umsg; + if (copy_from_user(&umsg, p + i, sizeof(umsg))) { + kfree(rdwr_pa); + return -EFAULT; + } + rdwr_pa[i] = (struct i2c_msg) { + .addr = umsg.addr, + .flags = umsg.flags, + .len = umsg.len, + .buf = compat_ptr(umsg.buf) + }; + } + + return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa); + } + case I2C_SMBUS: { + struct i2c_smbus_ioctl_data32 data32; + if (copy_from_user(&data32, + (void __user *) arg, + sizeof(data32))) + return -EFAULT; + return i2cdev_ioctl_smbus(client, data32.read_write, + data32.command, + data32.size, + compat_ptr(data32.data)); + } + default: + return i2cdev_ioctl(file, cmd, arg); + } +} +#else +#define compat_i2cdev_ioctl NULL +#endif + +static int i2cdev_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct i2c_client *client; + struct i2c_adapter *adap; + + adap = i2c_get_adapter(minor); + if (!adap) + return -ENODEV; + + /* This creates an anonymous i2c_client, which may later be + * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. + * + * This client is ** NEVER REGISTERED ** with the driver model + * or I2C core code!! It just holds private copies of addressing + * information and maybe a PEC flag. + */ + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) { + i2c_put_adapter(adap); + return -ENOMEM; + } + snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); + + client->adapter = adap; + file->private_data = client; + + return 0; +} + +static int i2cdev_release(struct inode *inode, struct file *file) +{ + struct i2c_client *client = file->private_data; + + i2c_put_adapter(client->adapter); + kfree(client); + file->private_data = NULL; + + return 0; +} + +static const struct file_operations i2cdev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = i2cdev_read, + .write = i2cdev_write, + .unlocked_ioctl = i2cdev_ioctl, + .compat_ioctl = compat_i2cdev_ioctl, + .open = i2cdev_open, + .release = i2cdev_release, +}; + +/* ------------------------------------------------------------------------- */ + +static struct class *i2c_dev_class; + +static int i2cdev_attach_adapter(struct device *dev, void *dummy) +{ + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + int res; + + if (dev->type != &i2c_adapter_type) + return 0; + adap = to_i2c_adapter(dev); + + i2c_dev = get_free_i2c_dev(adap); + if (IS_ERR(i2c_dev)) + return PTR_ERR(i2c_dev); + + cdev_init(&i2c_dev->cdev, &i2cdev_fops); + i2c_dev->cdev.owner = THIS_MODULE; + res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1); + if (res) + goto error_cdev; + + /* register this i2c device with the driver core */ + i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, + MKDEV(I2C_MAJOR, adap->nr), NULL, + "i2c-%d", adap->nr); + if (IS_ERR(i2c_dev->dev)) { + res = PTR_ERR(i2c_dev->dev); + goto error; + } + + pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", + adap->name, adap->nr); + return 0; +error: + cdev_del(&i2c_dev->cdev); +error_cdev: + put_i2c_dev(i2c_dev); + return res; +} + +static int i2cdev_detach_adapter(struct device *dev, void *dummy) +{ + struct i2c_adapter *adap; + struct i2c_dev *i2c_dev; + + if (dev->type != &i2c_adapter_type) + return 0; + adap = to_i2c_adapter(dev); + + i2c_dev = i2c_dev_get_by_minor(adap->nr); + if (!i2c_dev) /* attach_adapter must have failed */ + return 0; + + cdev_del(&i2c_dev->cdev); + put_i2c_dev(i2c_dev); + device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); + + pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); + return 0; +} + +static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + return i2cdev_attach_adapter(dev, NULL); + case BUS_NOTIFY_DEL_DEVICE: + return i2cdev_detach_adapter(dev, NULL); + } + + return 0; +} + +static struct notifier_block i2cdev_notifier = { + .notifier_call = i2cdev_notifier_call, +}; + +/* ------------------------------------------------------------------------- */ + +/* + * module load/unload record keeping + */ + +static int __init i2c_dev_init(void) +{ + int res; + + printk(KERN_INFO "i2c /dev entries driver\n"); + + res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); + if (res) + goto out; + + i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); + if (IS_ERR(i2c_dev_class)) { + res = PTR_ERR(i2c_dev_class); + goto out_unreg_chrdev; + } + i2c_dev_class->dev_groups = i2c_groups; + + /* Keep track of adapters which will be added or removed later */ + res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); + if (res) + goto out_unreg_class; + + /* Bind to already existing adapters right away */ + i2c_for_each_dev(NULL, i2cdev_attach_adapter); + + return 0; + +out_unreg_class: + class_destroy(i2c_dev_class); +out_unreg_chrdev: + unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); +out: + printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); + return res; +} + +static void __exit i2c_dev_exit(void) +{ + bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier); + i2c_for_each_dev(NULL, i2cdev_detach_adapter); + class_destroy(i2c_dev_class); + unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); +} + +MODULE_AUTHOR("Frodo Looijaard and " + "Simon G. Vogl "); +MODULE_DESCRIPTION("I2C /dev entries driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_dev_init); +module_exit(i2c_dev_exit); diff --git a/t/tree/drivers/i2c/i2c-smbus.c b/t/tree/drivers/i2c/i2c-smbus.c new file mode 100644 index 00000000..03096f47 --- /dev/null +++ b/t/tree/drivers/i2c/i2c-smbus.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c-smbus.c - SMBus extensions to the I2C protocol + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2010 Jean Delvare + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct i2c_smbus_alert { + struct work_struct alert; + struct i2c_client *ara; /* Alert response address */ +}; + +struct alert_data { + unsigned short addr; + enum i2c_alert_protocol type; + unsigned int data; +}; + +/* If this is the alerting device, notify its driver */ +static int smbus_do_alert(struct device *dev, void *addrp) +{ + struct i2c_client *client = i2c_verify_client(dev); + struct alert_data *data = addrp; + struct i2c_driver *driver; + + if (!client || client->addr != data->addr) + return 0; + if (client->flags & I2C_CLIENT_TEN) + return 0; + + /* + * Drivers should either disable alerts, or provide at least + * a minimal handler. Lock so the driver won't change. + */ + device_lock(dev); + if (client->dev.driver) { + driver = to_i2c_driver(client->dev.driver); + if (driver->alert) + driver->alert(client, data->type, data->data); + else + dev_warn(&client->dev, "no driver alert()!\n"); + } else + dev_dbg(&client->dev, "alert with no driver\n"); + device_unlock(dev); + + /* Stop iterating after we find the device */ + return -EBUSY; +} + +/* + * The alert IRQ handler needs to hand work off to a task which can issue + * SMBus calls, because those sleeping calls can't be made in IRQ context. + */ +static irqreturn_t smbus_alert(int irq, void *d) +{ + struct i2c_smbus_alert *alert = d; + struct i2c_client *ara; + unsigned short prev_addr = 0; /* Not a valid address */ + + ara = alert->ara; + + for (;;) { + s32 status; + struct alert_data data; + + /* + * Devices with pending alerts reply in address order, low + * to high, because of slave transmit arbitration. After + * responding, an SMBus device stops asserting SMBALERT#. + * + * Note that SMBus 2.0 reserves 10-bit addresses for future + * use. We neither handle them, nor try to use PEC here. + */ + status = i2c_smbus_read_byte(ara); + if (status < 0) + break; + + data.data = status & 1; + data.addr = status >> 1; + data.type = I2C_PROTOCOL_SMBUS_ALERT; + + if (data.addr == prev_addr) { + dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " + "0x%02x, skipping\n", data.addr); + break; + } + dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", + data.addr, data.data); + + /* Notify driver for the device which issued the alert */ + device_for_each_child(&ara->adapter->dev, &data, + smbus_do_alert); + prev_addr = data.addr; + } + + return IRQ_HANDLED; +} + +static void smbalert_work(struct work_struct *work) +{ + struct i2c_smbus_alert *alert; + + alert = container_of(work, struct i2c_smbus_alert, alert); + + smbus_alert(0, alert); + +} + +/* Setup SMBALERT# infrastructure */ +static int smbalert_probe(struct i2c_client *ara, + const struct i2c_device_id *id) +{ + struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev); + struct i2c_smbus_alert *alert; + struct i2c_adapter *adapter = ara->adapter; + int res, irq; + + alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert), + GFP_KERNEL); + if (!alert) + return -ENOMEM; + + if (setup) { + irq = setup->irq; + } else { + irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert"); + if (irq <= 0) + return irq; + } + + INIT_WORK(&alert->alert, smbalert_work); + alert->ara = ara; + + if (irq > 0) { + res = devm_request_threaded_irq(&ara->dev, irq, + NULL, smbus_alert, + IRQF_SHARED | IRQF_ONESHOT, + "smbus_alert", alert); + if (res) + return res; + } + + i2c_set_clientdata(ara, alert); + dev_info(&adapter->dev, "supports SMBALERT#\n"); + + return 0; +} + +/* IRQ and memory resources are managed so they are freed automatically */ +static int smbalert_remove(struct i2c_client *ara) +{ + struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); + + cancel_work_sync(&alert->alert); + return 0; +} + +static const struct i2c_device_id smbalert_ids[] = { + { "smbus_alert", 0 }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, smbalert_ids); + +static struct i2c_driver smbalert_driver = { + .driver = { + .name = "smbus_alert", + }, + .probe = smbalert_probe, + .remove = smbalert_remove, + .id_table = smbalert_ids, +}; + +/** + * i2c_handle_smbus_alert - Handle an SMBus alert + * @ara: the ARA client on the relevant adapter + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the alert work, in turn calling the + * corresponding I2C device driver's alert function. + * + * It is assumed that ara is a valid i2c client previously returned by + * i2c_setup_smbus_alert(). + */ +int i2c_handle_smbus_alert(struct i2c_client *ara) +{ + struct i2c_smbus_alert *alert = i2c_get_clientdata(ara); + + return schedule_work(&alert->alert); +} +EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); + +module_i2c_driver(smbalert_driver); + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("SMBus protocol extensions support"); +MODULE_LICENSE("GPL"); diff --git a/t/tree/drivers/i2c/i2c-stub.c b/t/tree/drivers/i2c/i2c-stub.c new file mode 100644 index 00000000..537a598e --- /dev/null +++ b/t/tree/drivers/i2c/i2c-stub.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + i2c-stub.c - I2C/SMBus chip emulator + + Copyright (c) 2004 Mark M. Hoffman + Copyright (C) 2007-2014 Jean Delvare + +*/ + +#define DEBUG 1 +#define pr_fmt(fmt) "i2c-stub: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CHIPS 10 + +/* + * Support for I2C_FUNC_SMBUS_BLOCK_DATA is disabled by default and must + * be enabled explicitly by setting the I2C_FUNC_SMBUS_BLOCK_DATA bits + * in the 'functionality' module parameter. + */ +#define STUB_FUNC_DEFAULT \ + (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK) + +#define STUB_FUNC_ALL \ + (STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA) + +static unsigned short chip_addr[MAX_CHIPS]; +module_param_array(chip_addr, ushort, NULL, S_IRUGO); +MODULE_PARM_DESC(chip_addr, + "Chip addresses (up to 10, between 0x03 and 0x77)"); + +static unsigned long functionality = STUB_FUNC_DEFAULT; +module_param(functionality, ulong, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(functionality, "Override functionality bitfield"); + +/* Some chips have banked register ranges */ + +static u8 bank_reg[MAX_CHIPS]; +module_param_array(bank_reg, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_reg, "Bank register"); + +static u8 bank_mask[MAX_CHIPS]; +module_param_array(bank_mask, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_mask, "Bank value mask"); + +static u8 bank_start[MAX_CHIPS]; +module_param_array(bank_start, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_start, "First banked register"); + +static u8 bank_end[MAX_CHIPS]; +module_param_array(bank_end, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(bank_end, "Last banked register"); + +struct smbus_block_data { + struct list_head node; + u8 command; + u8 len; + u8 block[I2C_SMBUS_BLOCK_MAX]; +}; + +struct stub_chip { + u8 pointer; + u16 words[256]; /* Byte operations use the LSB as per SMBus + specification */ + struct list_head smbus_blocks; + + /* For chips with banks, extra registers are allocated dynamically */ + u8 bank_reg; + u8 bank_shift; + u8 bank_mask; + u8 bank_sel; /* Currently selected bank */ + u8 bank_start; + u8 bank_end; + u16 bank_size; + u16 *bank_words; /* Room for bank_mask * bank_size registers */ +}; + +static struct stub_chip *stub_chips; +static int stub_chips_nr; + +static struct smbus_block_data *stub_find_block(struct device *dev, + struct stub_chip *chip, + u8 command, bool create) +{ + struct smbus_block_data *b, *rb = NULL; + + list_for_each_entry(b, &chip->smbus_blocks, node) { + if (b->command == command) { + rb = b; + break; + } + } + if (rb == NULL && create) { + rb = devm_kzalloc(dev, sizeof(*rb), GFP_KERNEL); + if (rb == NULL) + return rb; + rb->command = command; + list_add(&rb->node, &chip->smbus_blocks); + } + return rb; +} + +static u16 *stub_get_wordp(struct stub_chip *chip, u8 offset) +{ + if (chip->bank_sel && + offset >= chip->bank_start && offset <= chip->bank_end) + return chip->bank_words + + (chip->bank_sel - 1) * chip->bank_size + + offset - chip->bank_start; + else + return chip->words + offset; +} + +/* Return negative errno on error. */ +static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, union i2c_smbus_data *data) +{ + s32 ret; + int i, len; + struct stub_chip *chip = NULL; + struct smbus_block_data *b; + u16 *wordp; + + /* Search for the right chip */ + for (i = 0; i < stub_chips_nr; i++) { + if (addr == chip_addr[i]) { + chip = stub_chips + i; + break; + } + } + if (!chip) + return -ENODEV; + + switch (size) { + + case I2C_SMBUS_QUICK: + dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); + ret = 0; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + chip->pointer = command; + dev_dbg(&adap->dev, + "smbus byte - addr 0x%02x, wrote 0x%02x.\n", + addr, command); + } else { + wordp = stub_get_wordp(chip, chip->pointer++); + data->byte = *wordp & 0xff; + dev_dbg(&adap->dev, + "smbus byte - addr 0x%02x, read 0x%02x.\n", + addr, data->byte); + } + + ret = 0; + break; + + case I2C_SMBUS_BYTE_DATA: + wordp = stub_get_wordp(chip, command); + if (read_write == I2C_SMBUS_WRITE) { + *wordp &= 0xff00; + *wordp |= data->byte; + dev_dbg(&adap->dev, + "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n", + addr, data->byte, command); + + /* Set the bank as needed */ + if (chip->bank_words && command == chip->bank_reg) { + chip->bank_sel = + (data->byte >> chip->bank_shift) + & chip->bank_mask; + dev_dbg(&adap->dev, + "switching to bank %u.\n", + chip->bank_sel); + } + } else { + data->byte = *wordp & 0xff; + dev_dbg(&adap->dev, + "smbus byte data - addr 0x%02x, read 0x%02x at 0x%02x.\n", + addr, data->byte, command); + } + chip->pointer = command + 1; + + ret = 0; + break; + + case I2C_SMBUS_WORD_DATA: + wordp = stub_get_wordp(chip, command); + if (read_write == I2C_SMBUS_WRITE) { + *wordp = data->word; + dev_dbg(&adap->dev, + "smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n", + addr, data->word, command); + } else { + data->word = *wordp; + dev_dbg(&adap->dev, + "smbus word data - addr 0x%02x, read 0x%04x at 0x%02x.\n", + addr, data->word, command); + } + + ret = 0; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + /* + * We ignore banks here, because banked chips don't use I2C + * block transfers + */ + if (data->block[0] > 256 - command) /* Avoid overrun */ + data->block[0] = 256 - command; + len = data->block[0]; + if (read_write == I2C_SMBUS_WRITE) { + for (i = 0; i < len; i++) { + chip->words[command + i] &= 0xff00; + chip->words[command + i] |= data->block[1 + i]; + } + dev_dbg(&adap->dev, + "i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + for (i = 0; i < len; i++) { + data->block[1 + i] = + chip->words[command + i] & 0xff; + } + dev_dbg(&adap->dev, + "i2c block data - addr 0x%02x, read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + + case I2C_SMBUS_BLOCK_DATA: + /* + * We ignore banks here, because chips typically don't use both + * banks and SMBus block transfers + */ + b = stub_find_block(&adap->dev, chip, command, false); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) { + ret = -EINVAL; + break; + } + if (b == NULL) { + b = stub_find_block(&adap->dev, chip, command, + true); + if (b == NULL) { + ret = -ENOMEM; + break; + } + } + /* Largest write sets read block length */ + if (len > b->len) + b->len = len; + for (i = 0; i < len; i++) + b->block[i] = data->block[i + 1]; + /* update for byte and word commands */ + chip->words[command] = (b->block[0] << 8) | b->len; + dev_dbg(&adap->dev, + "smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n", + addr, len, command); + } else { + if (b == NULL) { + dev_dbg(&adap->dev, + "SMBus block read command without prior block write not supported\n"); + ret = -EOPNOTSUPP; + break; + } + len = b->len; + data->block[0] = len; + for (i = 0; i < len; i++) + data->block[i + 1] = b->block[i]; + dev_dbg(&adap->dev, + "smbus block data - addr 0x%02x, read %d bytes at 0x%02x.\n", + addr, len, command); + } + + ret = 0; + break; + + default: + dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); + ret = -EOPNOTSUPP; + break; + } /* switch (size) */ + + return ret; +} + +static u32 stub_func(struct i2c_adapter *adapter) +{ + return STUB_FUNC_ALL & functionality; +} + +static const struct i2c_algorithm smbus_algorithm = { + .functionality = stub_func, + .smbus_xfer = stub_xfer, +}; + +static struct i2c_adapter stub_adapter = { + .owner = THIS_MODULE, + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &smbus_algorithm, + .name = "SMBus stub driver", +}; + +static int __init i2c_stub_allocate_banks(int i) +{ + struct stub_chip *chip = stub_chips + i; + + chip->bank_reg = bank_reg[i]; + chip->bank_start = bank_start[i]; + chip->bank_end = bank_end[i]; + chip->bank_size = bank_end[i] - bank_start[i] + 1; + + /* We assume that all bits in the mask are contiguous */ + chip->bank_mask = bank_mask[i]; + while (!(chip->bank_mask & 1)) { + chip->bank_shift++; + chip->bank_mask >>= 1; + } + + chip->bank_words = kcalloc(chip->bank_mask * chip->bank_size, + sizeof(u16), + GFP_KERNEL); + if (!chip->bank_words) + return -ENOMEM; + + pr_debug("Allocated %u banks of %u words each (registers 0x%02x to 0x%02x)\n", + chip->bank_mask, chip->bank_size, chip->bank_start, + chip->bank_end); + + return 0; +} + +static void i2c_stub_free(void) +{ + int i; + + for (i = 0; i < stub_chips_nr; i++) + kfree(stub_chips[i].bank_words); + kfree(stub_chips); +} + +static int __init i2c_stub_init(void) +{ + int i, ret; + + if (!chip_addr[0]) { + pr_err("Please specify a chip address\n"); + return -ENODEV; + } + + for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { + if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { + pr_err("Invalid chip address 0x%02x\n", + chip_addr[i]); + return -EINVAL; + } + + pr_info("Virtual chip at 0x%02x\n", chip_addr[i]); + } + + /* Allocate memory for all chips at once */ + stub_chips_nr = i; + stub_chips = kcalloc(stub_chips_nr, sizeof(struct stub_chip), + GFP_KERNEL); + if (!stub_chips) + return -ENOMEM; + + for (i = 0; i < stub_chips_nr; i++) { + INIT_LIST_HEAD(&stub_chips[i].smbus_blocks); + + /* Allocate extra memory for banked register ranges */ + if (bank_mask[i]) { + ret = i2c_stub_allocate_banks(i); + if (ret) + goto fail_free; + } + } + + ret = i2c_add_adapter(&stub_adapter); + if (ret) + goto fail_free; + + return 0; + + fail_free: + i2c_stub_free(); + return ret; +} + +static void __exit i2c_stub_exit(void) +{ + i2c_del_adapter(&stub_adapter); + i2c_stub_free(); +} + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("I2C stub driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_stub_init); +module_exit(i2c_stub_exit); diff --git a/t/tree/include/acpi/acpi_bus.h b/t/tree/include/acpi/acpi_bus.h new file mode 100644 index 00000000..175f7b40 --- /dev/null +++ b/t/tree/include/acpi/acpi_bus.h @@ -0,0 +1,697 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * acpi_bus.h - ACPI Bus Driver ($Revision: 22 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + */ + +#ifndef __ACPI_BUS_H__ +#define __ACPI_BUS_H__ + +#include +#include + +/* TBD: Make dynamic */ +#define ACPI_MAX_HANDLES 10 +struct acpi_handle_list { + u32 count; + acpi_handle handles[ACPI_MAX_HANDLES]; +}; + +/* acpi_utils.h */ +acpi_status +acpi_extract_package(union acpi_object *package, + struct acpi_buffer *format, struct acpi_buffer *buffer); +acpi_status +acpi_evaluate_integer(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, unsigned long long *data); +acpi_status +acpi_evaluate_reference(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, + struct acpi_handle_list *list); +acpi_status +acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, + struct acpi_buffer *status_buf); + +acpi_status +acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld); + +bool acpi_has_method(acpi_handle handle, char *name); +acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, + u64 arg); +acpi_status acpi_evaluate_ej0(acpi_handle handle); +acpi_status acpi_evaluate_lck(acpi_handle handle, int lock); +bool acpi_ata_match(acpi_handle handle); +bool acpi_bay_match(acpi_handle handle); +bool acpi_dock_match(acpi_handle handle); + +bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs); +union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, + u64 rev, u64 func, union acpi_object *argv4); + +static inline union acpi_object * +acpi_evaluate_dsm_typed(acpi_handle handle, const guid_t *guid, u64 rev, + u64 func, union acpi_object *argv4, + acpi_object_type type) +{ + union acpi_object *obj; + + obj = acpi_evaluate_dsm(handle, guid, rev, func, argv4); + if (obj && obj->type != type) { + ACPI_FREE(obj); + obj = NULL; + } + + return obj; +} + +#define ACPI_INIT_DSM_ARGV4(cnt, eles) \ + { \ + .package.type = ACPI_TYPE_PACKAGE, \ + .package.count = (cnt), \ + .package.elements = (eles) \ + } + +bool acpi_dev_found(const char *hid); +bool acpi_dev_present(const char *hid, const char *uid, s64 hrv); + +struct acpi_device * +acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv); + +#ifdef CONFIG_ACPI + +#include + +#define ACPI_BUS_FILE_ROOT "acpi" +extern struct proc_dir_entry *acpi_root_dir; + +enum acpi_bus_device_type { + ACPI_BUS_TYPE_DEVICE = 0, + ACPI_BUS_TYPE_POWER, + ACPI_BUS_TYPE_PROCESSOR, + ACPI_BUS_TYPE_THERMAL, + ACPI_BUS_TYPE_POWER_BUTTON, + ACPI_BUS_TYPE_SLEEP_BUTTON, + ACPI_BUS_TYPE_ECDT_EC, + ACPI_BUS_DEVICE_TYPE_COUNT +}; + +struct acpi_driver; +struct acpi_device; + +/* + * ACPI Scan Handler + * ----------------- + */ + +struct acpi_hotplug_profile { + struct kobject kobj; + int (*scan_dependent)(struct acpi_device *adev); + void (*notify_online)(struct acpi_device *adev); + bool enabled:1; + bool demand_offline:1; +}; + +static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( + struct kobject *kobj) +{ + return container_of(kobj, struct acpi_hotplug_profile, kobj); +} + +struct acpi_scan_handler { + const struct acpi_device_id *ids; + struct list_head list_node; + bool (*match)(const char *idstr, const struct acpi_device_id **matchid); + int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); + void (*detach)(struct acpi_device *dev); + void (*bind)(struct device *phys_dev); + void (*unbind)(struct device *phys_dev); + struct acpi_hotplug_profile hotplug; +}; + +/* + * ACPI Hotplug Context + * -------------------- + */ + +struct acpi_hotplug_context { + struct acpi_device *self; + int (*notify)(struct acpi_device *, u32); + void (*uevent)(struct acpi_device *, u32); + void (*fixup)(struct acpi_device *); +}; + +/* + * ACPI Driver + * ----------- + */ + +typedef int (*acpi_op_add) (struct acpi_device * device); +typedef int (*acpi_op_remove) (struct acpi_device * device); +typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); + +struct acpi_device_ops { + acpi_op_add add; + acpi_op_remove remove; + acpi_op_notify notify; +}; + +#define ACPI_DRIVER_ALL_NOTIFY_EVENTS 0x1 /* system AND device events */ + +struct acpi_driver { + char name[80]; + char class[80]; + const struct acpi_device_id *ids; /* Supported Hardware IDs */ + unsigned int flags; + struct acpi_device_ops ops; + struct device_driver drv; + struct module *owner; +}; + +/* + * ACPI Device + * ----------- + */ + +/* Status (_STA) */ + +struct acpi_device_status { + u32 present:1; + u32 enabled:1; + u32 show_in_ui:1; + u32 functional:1; + u32 battery_present:1; + u32 reserved:27; +}; + +/* Flags */ + +struct acpi_device_flags { + u32 dynamic_status:1; + u32 removable:1; + u32 ejectable:1; + u32 power_manageable:1; + u32 match_driver:1; + u32 initialized:1; + u32 visited:1; + u32 hotplug_notify:1; + u32 is_dock_station:1; + u32 of_compatible_ok:1; + u32 coherent_dma:1; + u32 cca_seen:1; + u32 enumeration_by_parent:1; + u32 reserved:19; +}; + +/* File System */ + +struct acpi_device_dir { + struct proc_dir_entry *entry; +}; + +#define acpi_device_dir(d) ((d)->dir.entry) + +/* Plug and Play */ + +typedef char acpi_bus_id[8]; +typedef u64 acpi_bus_address; +typedef char acpi_device_name[40]; +typedef char acpi_device_class[20]; + +struct acpi_hardware_id { + struct list_head list; + const char *id; +}; + +struct acpi_pnp_type { + u32 hardware_id:1; + u32 bus_address:1; + u32 platform_id:1; + u32 reserved:29; +}; + +struct acpi_device_pnp { + acpi_bus_id bus_id; /* Object name */ + struct acpi_pnp_type type; /* ID type */ + acpi_bus_address bus_address; /* _ADR */ + char *unique_id; /* _UID */ + struct list_head ids; /* _HID and _CIDs */ + acpi_device_name device_name; /* Driver-determined */ + acpi_device_class device_class; /* " */ + union acpi_object *str_obj; /* unicode string for _STR method */ +}; + +#define acpi_device_bid(d) ((d)->pnp.bus_id) +#define acpi_device_adr(d) ((d)->pnp.bus_address) +const char *acpi_device_hid(struct acpi_device *device); +#define acpi_device_uid(d) ((d)->pnp.unique_id) +#define acpi_device_name(d) ((d)->pnp.device_name) +#define acpi_device_class(d) ((d)->pnp.device_class) + +/* Power Management */ + +struct acpi_device_power_flags { + u32 explicit_get:1; /* _PSC present? */ + u32 power_resources:1; /* Power resources */ + u32 inrush_current:1; /* Serialize Dx->D0 */ + u32 power_removed:1; /* Optimize Dx->D0 */ + u32 ignore_parent:1; /* Power is independent of parent power state */ + u32 dsw_present:1; /* _DSW present? */ + u32 reserved:26; +}; + +struct acpi_device_power_state { + struct { + u8 valid:1; + u8 explicit_set:1; /* _PSx present? */ + u8 reserved:6; + } flags; + int power; /* % Power (compared to D0) */ + int latency; /* Dx->D0 time (microseconds) */ + struct list_head resources; /* Power resources referenced */ +}; + +struct acpi_device_power { + int state; /* Current state */ + struct acpi_device_power_flags flags; + struct acpi_device_power_state states[ACPI_D_STATE_COUNT]; /* Power states (D0-D3Cold) */ +}; + +/* Performance Management */ + +struct acpi_device_perf_flags { + u8 reserved:8; +}; + +struct acpi_device_perf_state { + struct { + u8 valid:1; + u8 reserved:7; + } flags; + u8 power; /* % Power (compared to P0) */ + u8 performance; /* % Performance ( " ) */ + int latency; /* Px->P0 time (microseconds) */ +}; + +struct acpi_device_perf { + int state; + struct acpi_device_perf_flags flags; + int state_count; + struct acpi_device_perf_state *states; +}; + +/* Wakeup Management */ +struct acpi_device_wakeup_flags { + u8 valid:1; /* Can successfully enable wakeup? */ + u8 notifier_present:1; /* Wake-up notify handler has been installed */ +}; + +struct acpi_device_wakeup_context { + void (*func)(struct acpi_device_wakeup_context *context); + struct device *dev; +}; + +struct acpi_device_wakeup { + acpi_handle gpe_device; + u64 gpe_number; + u64 sleep_state; + struct list_head resources; + struct acpi_device_wakeup_flags flags; + struct acpi_device_wakeup_context context; + struct wakeup_source *ws; + int prepare_count; + int enable_count; +}; + +struct acpi_device_physical_node { + unsigned int node_id; + struct list_head node; + struct device *dev; + bool put_online:1; +}; + +struct acpi_device_properties { + const guid_t *guid; + const union acpi_object *properties; + struct list_head list; +}; + +/* ACPI Device Specific Data (_DSD) */ +struct acpi_device_data { + const union acpi_object *pointer; + struct list_head properties; + const union acpi_object *of_compatible; + struct list_head subnodes; +}; + +struct acpi_gpio_mapping; + +/* Device */ +struct acpi_device { + int device_type; + acpi_handle handle; /* no handle for fixed hardware */ + struct fwnode_handle fwnode; + struct acpi_device *parent; + struct list_head children; + struct list_head node; + struct list_head wakeup_list; + struct list_head del_list; + struct acpi_device_status status; + struct acpi_device_flags flags; + struct acpi_device_pnp pnp; + struct acpi_device_power power; + struct acpi_device_wakeup wakeup; + struct acpi_device_perf performance; + struct acpi_device_dir dir; + struct acpi_device_data data; + struct acpi_scan_handler *handler; + struct acpi_hotplug_context *hp; + struct acpi_driver *driver; + const struct acpi_gpio_mapping *driver_gpios; + void *driver_data; + struct device dev; + unsigned int physical_node_count; + unsigned int dep_unmet; + struct list_head physical_node_list; + struct mutex physical_node_lock; + void (*remove)(struct acpi_device *); +}; + +/* Non-device subnode */ +struct acpi_data_node { + const char *name; + acpi_handle handle; + struct fwnode_handle fwnode; + struct fwnode_handle *parent; + struct acpi_device_data data; + struct list_head sibling; + struct kobject kobj; + struct completion kobj_done; +}; + +extern const struct fwnode_operations acpi_device_fwnode_ops; +extern const struct fwnode_operations acpi_data_fwnode_ops; +extern const struct fwnode_operations acpi_static_fwnode_ops; + +bool is_acpi_device_node(const struct fwnode_handle *fwnode); +bool is_acpi_data_node(const struct fwnode_handle *fwnode); + +static inline bool is_acpi_node(const struct fwnode_handle *fwnode) +{ + return (is_acpi_device_node(fwnode) || is_acpi_data_node(fwnode)); +} + +#define to_acpi_device_node(__fwnode) \ + ({ \ + typeof(__fwnode) __to_acpi_device_node_fwnode = __fwnode; \ + \ + is_acpi_device_node(__to_acpi_device_node_fwnode) ? \ + container_of(__to_acpi_device_node_fwnode, \ + struct acpi_device, fwnode) : \ + NULL; \ + }) + +#define to_acpi_data_node(__fwnode) \ + ({ \ + typeof(__fwnode) __to_acpi_data_node_fwnode = __fwnode; \ + \ + is_acpi_data_node(__to_acpi_data_node_fwnode) ? \ + container_of(__to_acpi_data_node_fwnode, \ + struct acpi_data_node, fwnode) : \ + NULL; \ + }) + +static inline bool is_acpi_static_node(const struct fwnode_handle *fwnode) +{ + return !IS_ERR_OR_NULL(fwnode) && + fwnode->ops == &acpi_static_fwnode_ops; +} + +static inline bool acpi_data_node_match(const struct fwnode_handle *fwnode, + const char *name) +{ + return is_acpi_data_node(fwnode) ? + (!strcmp(to_acpi_data_node(fwnode)->name, name)) : false; +} + +static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) +{ + return &adev->fwnode; +} + +static inline void *acpi_driver_data(struct acpi_device *d) +{ + return d->driver_data; +} + +#define to_acpi_device(d) container_of(d, struct acpi_device, dev) +#define to_acpi_driver(d) container_of(d, struct acpi_driver, drv) + +static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta) +{ + *((u32 *)&adev->status) = sta; +} + +static inline void acpi_set_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp) +{ + hp->self = adev; + adev->hp = hp; +} + +void acpi_initialize_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp, + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32)); + +/* acpi_device.dev.bus == &acpi_bus_type */ +extern struct bus_type acpi_bus_type; + +/* + * Events + * ------ + */ + +struct acpi_bus_event { + struct list_head node; + acpi_device_class device_class; + acpi_bus_id bus_id; + u32 type; + u32 data; +}; + +extern struct kobject *acpi_kobj; +extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); +void acpi_bus_private_data_handler(acpi_handle, void *); +int acpi_bus_get_private_data(acpi_handle, void **); +int acpi_bus_attach_private_data(acpi_handle, void *); +void acpi_bus_detach_private_data(acpi_handle); +extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); +extern int register_acpi_notifier(struct notifier_block *); +extern int unregister_acpi_notifier(struct notifier_block *); + +/* + * External Functions + */ + +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device); +struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle); +void acpi_bus_put_acpi_device(struct acpi_device *adev); +acpi_status acpi_bus_get_status_handle(acpi_handle handle, + unsigned long long *sta); +int acpi_bus_get_status(struct acpi_device *device); + +int acpi_bus_set_power(acpi_handle handle, int state); +const char *acpi_power_state_string(int state); +int acpi_device_set_power(struct acpi_device *device, int state); +int acpi_bus_init_power(struct acpi_device *device); +int acpi_device_fix_up_power(struct acpi_device *device); +int acpi_bus_update_power(acpi_handle handle, int *state_p); +int acpi_device_update_power(struct acpi_device *device, int *state_p); +bool acpi_bus_power_manageable(acpi_handle handle); +int acpi_device_power_add_dependent(struct acpi_device *adev, + struct device *dev); +void acpi_device_power_remove_dependent(struct acpi_device *adev, + struct device *dev); + +#ifdef CONFIG_PM +bool acpi_bus_can_wakeup(acpi_handle handle); +#else +static inline bool acpi_bus_can_wakeup(acpi_handle handle) { return false; } +#endif + +void acpi_scan_lock_acquire(void); +void acpi_scan_lock_release(void); +void acpi_lock_hp_context(void); +void acpi_unlock_hp_context(void); +int acpi_scan_add_handler(struct acpi_scan_handler *handler); +int acpi_bus_register_driver(struct acpi_driver *driver); +void acpi_bus_unregister_driver(struct acpi_driver *driver); +int acpi_bus_scan(acpi_handle handle); +void acpi_bus_trim(struct acpi_device *start); +acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); +int acpi_match_device_ids(struct acpi_device *device, + const struct acpi_device_id *ids); +void acpi_set_modalias(struct acpi_device *adev, const char *default_id, + char *modalias, size_t len); +int acpi_create_dir(struct acpi_device *); +void acpi_remove_dir(struct acpi_device *); + +static inline bool acpi_device_enumerated(struct acpi_device *adev) +{ + return adev && adev->flags.initialized && adev->flags.visited; +} + +/** + * module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver + * @__acpi_driver: acpi_driver struct + * + * Helper macro for ACPI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_acpi_driver(__acpi_driver) \ + module_driver(__acpi_driver, acpi_bus_register_driver, \ + acpi_bus_unregister_driver) + +/* + * Bind physical devices with ACPI devices + */ +struct acpi_bus_type { + struct list_head list; + const char *name; + bool (*match)(struct device *dev); + struct acpi_device * (*find_companion)(struct device *); + void (*setup)(struct device *); + void (*cleanup)(struct device *); +}; +int register_acpi_bus_type(struct acpi_bus_type *); +int unregister_acpi_bus_type(struct acpi_bus_type *); +int acpi_bind_one(struct device *dev, struct acpi_device *adev); +int acpi_unbind_one(struct device *dev); + +struct acpi_pci_root { + struct acpi_device * device; + struct pci_bus *bus; + u16 segment; + struct resource secondary; /* downstream bus range */ + + u32 osc_support_set; /* _OSC state of support bits */ + u32 osc_control_set; /* _OSC state of control bits */ + phys_addr_t mcfg_addr; +}; + +/* helper */ + +bool acpi_dma_supported(struct acpi_device *adev); +enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev); +int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset, + u64 *size); +int acpi_dma_configure(struct device *dev, enum dev_dma_attr attr); + +struct acpi_device *acpi_find_child_device(struct acpi_device *parent, + u64 address, bool check_children); +int acpi_is_root_bridge(acpi_handle); +struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); + +int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); +int acpi_disable_wakeup_device_power(struct acpi_device *dev); + +#ifdef CONFIG_X86 +bool acpi_device_always_present(struct acpi_device *adev); +#else +static inline bool acpi_device_always_present(struct acpi_device *adev) +{ + return false; +} +#endif + +#ifdef CONFIG_PM +void acpi_pm_wakeup_event(struct device *dev); +acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, + void (*func)(struct acpi_device_wakeup_context *context)); +acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); +bool acpi_pm_device_can_wakeup(struct device *dev); +int acpi_pm_device_sleep_state(struct device *, int *, int); +int acpi_pm_set_device_wakeup(struct device *dev, bool enable); +int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable); +#else +static inline void acpi_pm_wakeup_event(struct device *dev) +{ +} +static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, + struct device *dev, + void (*func)(struct acpi_device_wakeup_context *context)) +{ + return AE_SUPPORT; +} +static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) +{ + return AE_SUPPORT; +} +static inline bool acpi_pm_device_can_wakeup(struct device *dev) +{ + return false; +} +static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) +{ + if (p) + *p = ACPI_STATE_D0; + + return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ? + m : ACPI_STATE_D0; +} +static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable) +{ + return -ENODEV; +} +static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT +bool acpi_sleep_state_supported(u8 sleep_state); +#else +static inline bool acpi_sleep_state_supported(u8 sleep_state) { return false; } +#endif + +#ifdef CONFIG_ACPI_SLEEP +u32 acpi_target_system_state(void); +#else +static inline u32 acpi_target_system_state(void) { return ACPI_STATE_S0; } +#endif + +static inline bool acpi_device_power_manageable(struct acpi_device *adev) +{ + return adev->flags.power_manageable; +} + +static inline bool acpi_device_can_wakeup(struct acpi_device *adev) +{ + return adev->wakeup.flags.valid; +} + +static inline bool acpi_device_can_poweroff(struct acpi_device *adev) +{ + return adev->power.states[ACPI_STATE_D3_COLD].flags.valid || + ((acpi_gbl_FADT.header.revision < 6) && + adev->power.states[ACPI_STATE_D3_HOT].flags.explicit_set); +} + +static inline void acpi_dev_put(struct acpi_device *adev) +{ + put_device(&adev->dev); +} +#else /* CONFIG_ACPI */ + +static inline int register_acpi_bus_type(void *bus) { return 0; } +static inline int unregister_acpi_bus_type(void *bus) { return 0; } + +#endif /* CONFIG_ACPI */ + +#endif /*__ACPI_BUS_H__*/ diff --git a/t/tree/include/acpi/acpi_drivers.h b/t/tree/include/acpi/acpi_drivers.h new file mode 100644 index 00000000..5eb17593 --- /dev/null +++ b/t/tree/include/acpi/acpi_drivers.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * acpi_drivers.h ($Revision: 31 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + */ + +#ifndef __ACPI_DRIVERS_H__ +#define __ACPI_DRIVERS_H__ + +#define ACPI_MAX_STRING 80 + +/* + * Please update drivers/acpi/debug.c and Documentation/firmware-guide/acpi/debug.rst + * if you add to this list. + */ +#define ACPI_BUS_COMPONENT 0x00010000 +#define ACPI_AC_COMPONENT 0x00020000 +#define ACPI_BATTERY_COMPONENT 0x00040000 +#define ACPI_BUTTON_COMPONENT 0x00080000 +#define ACPI_SBS_COMPONENT 0x00100000 +#define ACPI_FAN_COMPONENT 0x00200000 +#define ACPI_PCI_COMPONENT 0x00400000 +#define ACPI_POWER_COMPONENT 0x00800000 +#define ACPI_CONTAINER_COMPONENT 0x01000000 +#define ACPI_SYSTEM_COMPONENT 0x02000000 +#define ACPI_THERMAL_COMPONENT 0x04000000 +#define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000 +#define ACPI_VIDEO_COMPONENT 0x10000000 +#define ACPI_PROCESSOR_COMPONENT 0x20000000 + +/* + * _HID definitions + * HIDs must conform to ACPI spec(6.1.4) + * Linux specific HIDs do not apply to this and begin with LNX: + */ + +#define ACPI_POWER_HID "LNXPOWER" +#define ACPI_PROCESSOR_OBJECT_HID "LNXCPU" +#define ACPI_SYSTEM_HID "LNXSYSTM" +#define ACPI_THERMAL_HID "LNXTHERM" +#define ACPI_BUTTON_HID_POWERF "LNXPWRBN" +#define ACPI_BUTTON_HID_SLEEPF "LNXSLPBN" +#define ACPI_VIDEO_HID "LNXVIDEO" +#define ACPI_BAY_HID "LNXIOBAY" +#define ACPI_DOCK_HID "LNXDOCK" +#define ACPI_ECDT_HID "LNXEC" +/* Quirk for broken IBM BIOSes */ +#define ACPI_SMBUS_IBM_HID "SMBUSIBM" + +/* + * For fixed hardware buttons, we fabricate acpi_devices with HID + * ACPI_BUTTON_HID_POWERF or ACPI_BUTTON_HID_SLEEPF. Fixed hardware + * signals only an event; it doesn't supply a notification value. + * To allow drivers to treat notifications from fixed hardware the + * same as those from real devices, we turn the events into this + * notification value. + */ +#define ACPI_FIXED_HARDWARE_EVENT 0x100 + +/* -------------------------------------------------------------------------- + PCI + -------------------------------------------------------------------------- */ + + +/* ACPI PCI Interrupt Link (pci_link.c) */ + +int acpi_irq_penalty_init(void); +int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, + int *polarity, char **name); +int acpi_pci_link_free_irq(acpi_handle handle); + +/* ACPI PCI Device Binding (pci_bind.c) */ + +struct pci_bus; + +#ifdef CONFIG_PCI +struct pci_dev *acpi_get_pci_dev(acpi_handle); +#else +static inline struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +{ + return NULL; +} +#endif + +/* Arch-defined function to add a bus to the system */ + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root); + +#ifdef CONFIG_X86 +void pci_acpi_crs_quirks(void); +#else +static inline void pci_acpi_crs_quirks(void) { } +#endif + +/* -------------------------------------------------------------------------- + Processor + -------------------------------------------------------------------------- */ + +#define ACPI_PROCESSOR_LIMIT_NONE 0x00 +#define ACPI_PROCESSOR_LIMIT_INCREMENT 0x01 +#define ACPI_PROCESSOR_LIMIT_DECREMENT 0x02 + +/*-------------------------------------------------------------------------- + Dock Station + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_DOCK +extern int is_dock_device(struct acpi_device *adev); +#else +static inline int is_dock_device(struct acpi_device *adev) +{ + return 0; +} +#endif /* CONFIG_ACPI_DOCK */ + +#endif /*__ACPI_DRIVERS_H__*/ diff --git a/t/tree/include/asm-generic/barrier.h b/t/tree/include/asm-generic/barrier.h new file mode 100644 index 00000000..85b28eb8 --- /dev/null +++ b/t/tree/include/asm-generic/barrier.h @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Generic barrier definitions. + * + * It should be possible to use these on really simple architectures, + * but it serves more as a starting point for new ports. + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#ifndef __ASM_GENERIC_BARRIER_H +#define __ASM_GENERIC_BARRIER_H + +#ifndef __ASSEMBLY__ + +#include + +#ifndef nop +#define nop() asm volatile ("nop") +#endif + +/* + * Force strict CPU ordering. And yes, this is required on UP too when we're + * talking to devices. + * + * Fall back to compiler barriers if nothing better is provided. + */ + +#ifndef mb +#define mb() barrier() +#endif + +#ifndef rmb +#define rmb() mb() +#endif + +#ifndef wmb +#define wmb() mb() +#endif + +#ifndef dma_rmb +#define dma_rmb() rmb() +#endif + +#ifndef dma_wmb +#define dma_wmb() wmb() +#endif + +#ifndef read_barrier_depends +#define read_barrier_depends() do { } while (0) +#endif + +#ifndef __smp_mb +#define __smp_mb() mb() +#endif + +#ifndef __smp_rmb +#define __smp_rmb() rmb() +#endif + +#ifndef __smp_wmb +#define __smp_wmb() wmb() +#endif + +#ifndef __smp_read_barrier_depends +#define __smp_read_barrier_depends() read_barrier_depends() +#endif + +#ifdef CONFIG_SMP + +#ifndef smp_mb +#define smp_mb() __smp_mb() +#endif + +#ifndef smp_rmb +#define smp_rmb() __smp_rmb() +#endif + +#ifndef smp_wmb +#define smp_wmb() __smp_wmb() +#endif + +#ifndef smp_read_barrier_depends +#define smp_read_barrier_depends() __smp_read_barrier_depends() +#endif + +#else /* !CONFIG_SMP */ + +#ifndef smp_mb +#define smp_mb() barrier() +#endif + +#ifndef smp_rmb +#define smp_rmb() barrier() +#endif + +#ifndef smp_wmb +#define smp_wmb() barrier() +#endif + +#ifndef smp_read_barrier_depends +#define smp_read_barrier_depends() do { } while (0) +#endif + +#endif /* CONFIG_SMP */ + +#ifndef __smp_store_mb +#define __smp_store_mb(var, value) do { WRITE_ONCE(var, value); __smp_mb(); } while (0) +#endif + +#ifndef __smp_mb__before_atomic +#define __smp_mb__before_atomic() __smp_mb() +#endif + +#ifndef __smp_mb__after_atomic +#define __smp_mb__after_atomic() __smp_mb() +#endif + +#ifndef __smp_store_release +#define __smp_store_release(p, v) \ +do { \ + compiletime_assert_atomic_type(*p); \ + __smp_mb(); \ + WRITE_ONCE(*p, v); \ +} while (0) +#endif + +#ifndef __smp_load_acquire +#define __smp_load_acquire(p) \ +({ \ + typeof(*p) ___p1 = READ_ONCE(*p); \ + compiletime_assert_atomic_type(*p); \ + __smp_mb(); \ + ___p1; \ +}) +#endif + +#ifdef CONFIG_SMP + +#ifndef smp_store_mb +#define smp_store_mb(var, value) __smp_store_mb(var, value) +#endif + +#ifndef smp_mb__before_atomic +#define smp_mb__before_atomic() __smp_mb__before_atomic() +#endif + +#ifndef smp_mb__after_atomic +#define smp_mb__after_atomic() __smp_mb__after_atomic() +#endif + +#ifndef smp_store_release +#define smp_store_release(p, v) __smp_store_release(p, v) +#endif + +#ifndef smp_load_acquire +#define smp_load_acquire(p) __smp_load_acquire(p) +#endif + +#else /* !CONFIG_SMP */ + +#ifndef smp_store_mb +#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); barrier(); } while (0) +#endif + +#ifndef smp_mb__before_atomic +#define smp_mb__before_atomic() barrier() +#endif + +#ifndef smp_mb__after_atomic +#define smp_mb__after_atomic() barrier() +#endif + +#ifndef smp_store_release +#define smp_store_release(p, v) \ +do { \ + compiletime_assert_atomic_type(*p); \ + barrier(); \ + WRITE_ONCE(*p, v); \ +} while (0) +#endif + +#ifndef smp_load_acquire +#define smp_load_acquire(p) \ +({ \ + typeof(*p) ___p1 = READ_ONCE(*p); \ + compiletime_assert_atomic_type(*p); \ + barrier(); \ + ___p1; \ +}) +#endif + +#endif /* CONFIG_SMP */ + +/* Barriers for virtual machine guests when talking to an SMP host */ +#define virt_mb() __smp_mb() +#define virt_rmb() __smp_rmb() +#define virt_wmb() __smp_wmb() +#define virt_read_barrier_depends() __smp_read_barrier_depends() +#define virt_store_mb(var, value) __smp_store_mb(var, value) +#define virt_mb__before_atomic() __smp_mb__before_atomic() +#define virt_mb__after_atomic() __smp_mb__after_atomic() +#define virt_store_release(p, v) __smp_store_release(p, v) +#define virt_load_acquire(p) __smp_load_acquire(p) + +/** + * smp_acquire__after_ctrl_dep() - Provide ACQUIRE ordering after a control dependency + * + * A control dependency provides a LOAD->STORE order, the additional RMB + * provides LOAD->LOAD order, together they provide LOAD->{LOAD,STORE} order, + * aka. (load)-ACQUIRE. + * + * Architectures that do not do load speculation can have this be barrier(). + */ +#ifndef smp_acquire__after_ctrl_dep +#define smp_acquire__after_ctrl_dep() smp_rmb() +#endif + +/** + * smp_cond_load_relaxed() - (Spin) wait for cond with no ordering guarantees + * @ptr: pointer to the variable to wait on + * @cond: boolean expression to wait for + * + * Equivalent to using READ_ONCE() on the condition variable. + * + * Due to C lacking lambda expressions we load the value of *ptr into a + * pre-named variable @VAL to be used in @cond. + */ +#ifndef smp_cond_load_relaxed +#define smp_cond_load_relaxed(ptr, cond_expr) ({ \ + typeof(ptr) __PTR = (ptr); \ + typeof(*ptr) VAL; \ + for (;;) { \ + VAL = READ_ONCE(*__PTR); \ + if (cond_expr) \ + break; \ + cpu_relax(); \ + } \ + VAL; \ +}) +#endif + +/** + * smp_cond_load_acquire() - (Spin) wait for cond with ACQUIRE ordering + * @ptr: pointer to the variable to wait on + * @cond: boolean expression to wait for + * + * Equivalent to using smp_load_acquire() on the condition variable but employs + * the control dependency of the wait to reduce the barrier on many platforms. + */ +#ifndef smp_cond_load_acquire +#define smp_cond_load_acquire(ptr, cond_expr) ({ \ + typeof(*ptr) _val; \ + _val = smp_cond_load_relaxed(ptr, cond_expr); \ + smp_acquire__after_ctrl_dep(); \ + _val; \ +}) +#endif + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_GENERIC_BARRIER_H */ diff --git a/t/tree/include/asm-generic/io.h b/t/tree/include/asm-generic/io.h new file mode 100644 index 00000000..d0280651 --- /dev/null +++ b/t/tree/include/asm-generic/io.h @@ -0,0 +1,1124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Generic I/O port emulation. + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#ifndef __ASM_GENERIC_IO_H +#define __ASM_GENERIC_IO_H + +#include /* I/O is all done through memory accesses */ +#include /* for memset() and memcpy() */ +#include + +#ifdef CONFIG_GENERIC_IOMAP +#include +#endif + +#include +#include + +#ifndef __io_br +#define __io_br() barrier() +#endif + +/* prevent prefetching of coherent DMA data ahead of a dma-complete */ +#ifndef __io_ar +#ifdef rmb +#define __io_ar(v) rmb() +#else +#define __io_ar(v) barrier() +#endif +#endif + +/* flush writes to coherent DMA data before possibly triggering a DMA read */ +#ifndef __io_bw +#ifdef wmb +#define __io_bw() wmb() +#else +#define __io_bw() barrier() +#endif +#endif + +/* serialize device access against a spin_unlock, usually handled there. */ +#ifndef __io_aw +#define __io_aw() mmiowb_set_pending() +#endif + +#ifndef __io_pbw +#define __io_pbw() __io_bw() +#endif + +#ifndef __io_paw +#define __io_paw() __io_aw() +#endif + +#ifndef __io_pbr +#define __io_pbr() __io_br() +#endif + +#ifndef __io_par +#define __io_par(v) __io_ar(v) +#endif + + +/* + * __raw_{read,write}{b,w,l,q}() access memory in native endianness. + * + * On some architectures memory mapped IO needs to be accessed differently. + * On the simple architectures, we just read/write the memory location + * directly. + */ + +#ifndef __raw_readb +#define __raw_readb __raw_readb +static inline u8 __raw_readb(const volatile void __iomem *addr) +{ + return *(const volatile u8 __force *)addr; +} +#endif + +#ifndef __raw_readw +#define __raw_readw __raw_readw +static inline u16 __raw_readw(const volatile void __iomem *addr) +{ + return *(const volatile u16 __force *)addr; +} +#endif + +#ifndef __raw_readl +#define __raw_readl __raw_readl +static inline u32 __raw_readl(const volatile void __iomem *addr) +{ + return *(const volatile u32 __force *)addr; +} +#endif + +#ifdef CONFIG_64BIT +#ifndef __raw_readq +#define __raw_readq __raw_readq +static inline u64 __raw_readq(const volatile void __iomem *addr) +{ + return *(const volatile u64 __force *)addr; +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef __raw_writeb +#define __raw_writeb __raw_writeb +static inline void __raw_writeb(u8 value, volatile void __iomem *addr) +{ + *(volatile u8 __force *)addr = value; +} +#endif + +#ifndef __raw_writew +#define __raw_writew __raw_writew +static inline void __raw_writew(u16 value, volatile void __iomem *addr) +{ + *(volatile u16 __force *)addr = value; +} +#endif + +#ifndef __raw_writel +#define __raw_writel __raw_writel +static inline void __raw_writel(u32 value, volatile void __iomem *addr) +{ + *(volatile u32 __force *)addr = value; +} +#endif + +#ifdef CONFIG_64BIT +#ifndef __raw_writeq +#define __raw_writeq __raw_writeq +static inline void __raw_writeq(u64 value, volatile void __iomem *addr) +{ + *(volatile u64 __force *)addr = value; +} +#endif +#endif /* CONFIG_64BIT */ + +/* + * {read,write}{b,w,l,q}() access little endian memory and return result in + * native endianness. + */ + +#ifndef readb +#define readb readb +static inline u8 readb(const volatile void __iomem *addr) +{ + u8 val; + + __io_br(); + val = __raw_readb(addr); + __io_ar(val); + return val; +} +#endif + +#ifndef readw +#define readw readw +static inline u16 readw(const volatile void __iomem *addr) +{ + u16 val; + + __io_br(); + val = __le16_to_cpu(__raw_readw(addr)); + __io_ar(val); + return val; +} +#endif + +#ifndef readl +#define readl readl +static inline u32 readl(const volatile void __iomem *addr) +{ + u32 val; + + __io_br(); + val = __le32_to_cpu(__raw_readl(addr)); + __io_ar(val); + return val; +} +#endif + +#ifdef CONFIG_64BIT +#ifndef readq +#define readq readq +static inline u64 readq(const volatile void __iomem *addr) +{ + u64 val; + + __io_br(); + val = __le64_to_cpu(__raw_readq(addr)); + __io_ar(val); + return val; +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef writeb +#define writeb writeb +static inline void writeb(u8 value, volatile void __iomem *addr) +{ + __io_bw(); + __raw_writeb(value, addr); + __io_aw(); +} +#endif + +#ifndef writew +#define writew writew +static inline void writew(u16 value, volatile void __iomem *addr) +{ + __io_bw(); + __raw_writew(cpu_to_le16(value), addr); + __io_aw(); +} +#endif + +#ifndef writel +#define writel writel +static inline void writel(u32 value, volatile void __iomem *addr) +{ + __io_bw(); + __raw_writel(__cpu_to_le32(value), addr); + __io_aw(); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef writeq +#define writeq writeq +static inline void writeq(u64 value, volatile void __iomem *addr) +{ + __io_bw(); + __raw_writeq(__cpu_to_le64(value), addr); + __io_aw(); +} +#endif +#endif /* CONFIG_64BIT */ + +/* + * {read,write}{b,w,l,q}_relaxed() are like the regular version, but + * are not guaranteed to provide ordering against spinlocks or memory + * accesses. + */ +#ifndef readb_relaxed +#define readb_relaxed readb_relaxed +static inline u8 readb_relaxed(const volatile void __iomem *addr) +{ + return __raw_readb(addr); +} +#endif + +#ifndef readw_relaxed +#define readw_relaxed readw_relaxed +static inline u16 readw_relaxed(const volatile void __iomem *addr) +{ + return __le16_to_cpu(__raw_readw(addr)); +} +#endif + +#ifndef readl_relaxed +#define readl_relaxed readl_relaxed +static inline u32 readl_relaxed(const volatile void __iomem *addr) +{ + return __le32_to_cpu(__raw_readl(addr)); +} +#endif + +#if defined(readq) && !defined(readq_relaxed) +#define readq_relaxed readq_relaxed +static inline u64 readq_relaxed(const volatile void __iomem *addr) +{ + return __le64_to_cpu(__raw_readq(addr)); +} +#endif + +#ifndef writeb_relaxed +#define writeb_relaxed writeb_relaxed +static inline void writeb_relaxed(u8 value, volatile void __iomem *addr) +{ + __raw_writeb(value, addr); +} +#endif + +#ifndef writew_relaxed +#define writew_relaxed writew_relaxed +static inline void writew_relaxed(u16 value, volatile void __iomem *addr) +{ + __raw_writew(cpu_to_le16(value), addr); +} +#endif + +#ifndef writel_relaxed +#define writel_relaxed writel_relaxed +static inline void writel_relaxed(u32 value, volatile void __iomem *addr) +{ + __raw_writel(__cpu_to_le32(value), addr); +} +#endif + +#if defined(writeq) && !defined(writeq_relaxed) +#define writeq_relaxed writeq_relaxed +static inline void writeq_relaxed(u64 value, volatile void __iomem *addr) +{ + __raw_writeq(__cpu_to_le64(value), addr); +} +#endif + +/* + * {read,write}s{b,w,l,q}() repeatedly access the same memory address in + * native endianness in 8-, 16-, 32- or 64-bit chunks (@count times). + */ +#ifndef readsb +#define readsb readsb +static inline void readsb(const volatile void __iomem *addr, void *buffer, + unsigned int count) +{ + if (count) { + u8 *buf = buffer; + + do { + u8 x = __raw_readb(addr); + *buf++ = x; + } while (--count); + } +} +#endif + +#ifndef readsw +#define readsw readsw +static inline void readsw(const volatile void __iomem *addr, void *buffer, + unsigned int count) +{ + if (count) { + u16 *buf = buffer; + + do { + u16 x = __raw_readw(addr); + *buf++ = x; + } while (--count); + } +} +#endif + +#ifndef readsl +#define readsl readsl +static inline void readsl(const volatile void __iomem *addr, void *buffer, + unsigned int count) +{ + if (count) { + u32 *buf = buffer; + + do { + u32 x = __raw_readl(addr); + *buf++ = x; + } while (--count); + } +} +#endif + +#ifdef CONFIG_64BIT +#ifndef readsq +#define readsq readsq +static inline void readsq(const volatile void __iomem *addr, void *buffer, + unsigned int count) +{ + if (count) { + u64 *buf = buffer; + + do { + u64 x = __raw_readq(addr); + *buf++ = x; + } while (--count); + } +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef writesb +#define writesb writesb +static inline void writesb(volatile void __iomem *addr, const void *buffer, + unsigned int count) +{ + if (count) { + const u8 *buf = buffer; + + do { + __raw_writeb(*buf++, addr); + } while (--count); + } +} +#endif + +#ifndef writesw +#define writesw writesw +static inline void writesw(volatile void __iomem *addr, const void *buffer, + unsigned int count) +{ + if (count) { + const u16 *buf = buffer; + + do { + __raw_writew(*buf++, addr); + } while (--count); + } +} +#endif + +#ifndef writesl +#define writesl writesl +static inline void writesl(volatile void __iomem *addr, const void *buffer, + unsigned int count) +{ + if (count) { + const u32 *buf = buffer; + + do { + __raw_writel(*buf++, addr); + } while (--count); + } +} +#endif + +#ifdef CONFIG_64BIT +#ifndef writesq +#define writesq writesq +static inline void writesq(volatile void __iomem *addr, const void *buffer, + unsigned int count) +{ + if (count) { + const u64 *buf = buffer; + + do { + __raw_writeq(*buf++, addr); + } while (--count); + } +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef PCI_IOBASE +#define PCI_IOBASE ((void __iomem *)0) +#endif + +#ifndef IO_SPACE_LIMIT +#define IO_SPACE_LIMIT 0xffff +#endif + +#include + +/* + * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be + * implemented on hardware that needs an additional delay for I/O accesses to + * take effect. + */ + +#ifndef inb +#define inb inb +static inline u8 inb(unsigned long addr) +{ + u8 val; + + __io_pbr(); + val = __raw_readb(PCI_IOBASE + addr); + __io_par(val); + return val; +} +#endif + +#ifndef inw +#define inw inw +static inline u16 inw(unsigned long addr) +{ + u16 val; + + __io_pbr(); + val = __le16_to_cpu(__raw_readw(PCI_IOBASE + addr)); + __io_par(val); + return val; +} +#endif + +#ifndef inl +#define inl inl +static inline u32 inl(unsigned long addr) +{ + u32 val; + + __io_pbr(); + val = __le32_to_cpu(__raw_readl(PCI_IOBASE + addr)); + __io_par(val); + return val; +} +#endif + +#ifndef outb +#define outb outb +static inline void outb(u8 value, unsigned long addr) +{ + __io_pbw(); + __raw_writeb(value, PCI_IOBASE + addr); + __io_paw(); +} +#endif + +#ifndef outw +#define outw outw +static inline void outw(u16 value, unsigned long addr) +{ + __io_pbw(); + __raw_writew(cpu_to_le16(value), PCI_IOBASE + addr); + __io_paw(); +} +#endif + +#ifndef outl +#define outl outl +static inline void outl(u32 value, unsigned long addr) +{ + __io_pbw(); + __raw_writel(cpu_to_le32(value), PCI_IOBASE + addr); + __io_paw(); +} +#endif + +#ifndef inb_p +#define inb_p inb_p +static inline u8 inb_p(unsigned long addr) +{ + return inb(addr); +} +#endif + +#ifndef inw_p +#define inw_p inw_p +static inline u16 inw_p(unsigned long addr) +{ + return inw(addr); +} +#endif + +#ifndef inl_p +#define inl_p inl_p +static inline u32 inl_p(unsigned long addr) +{ + return inl(addr); +} +#endif + +#ifndef outb_p +#define outb_p outb_p +static inline void outb_p(u8 value, unsigned long addr) +{ + outb(value, addr); +} +#endif + +#ifndef outw_p +#define outw_p outw_p +static inline void outw_p(u16 value, unsigned long addr) +{ + outw(value, addr); +} +#endif + +#ifndef outl_p +#define outl_p outl_p +static inline void outl_p(u32 value, unsigned long addr) +{ + outl(value, addr); +} +#endif + +/* + * {in,out}s{b,w,l}{,_p}() are variants of the above that repeatedly access a + * single I/O port multiple times. + */ + +#ifndef insb +#define insb insb +static inline void insb(unsigned long addr, void *buffer, unsigned int count) +{ + readsb(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef insw +#define insw insw +static inline void insw(unsigned long addr, void *buffer, unsigned int count) +{ + readsw(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef insl +#define insl insl +static inline void insl(unsigned long addr, void *buffer, unsigned int count) +{ + readsl(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef outsb +#define outsb outsb +static inline void outsb(unsigned long addr, const void *buffer, + unsigned int count) +{ + writesb(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef outsw +#define outsw outsw +static inline void outsw(unsigned long addr, const void *buffer, + unsigned int count) +{ + writesw(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef outsl +#define outsl outsl +static inline void outsl(unsigned long addr, const void *buffer, + unsigned int count) +{ + writesl(PCI_IOBASE + addr, buffer, count); +} +#endif + +#ifndef insb_p +#define insb_p insb_p +static inline void insb_p(unsigned long addr, void *buffer, unsigned int count) +{ + insb(addr, buffer, count); +} +#endif + +#ifndef insw_p +#define insw_p insw_p +static inline void insw_p(unsigned long addr, void *buffer, unsigned int count) +{ + insw(addr, buffer, count); +} +#endif + +#ifndef insl_p +#define insl_p insl_p +static inline void insl_p(unsigned long addr, void *buffer, unsigned int count) +{ + insl(addr, buffer, count); +} +#endif + +#ifndef outsb_p +#define outsb_p outsb_p +static inline void outsb_p(unsigned long addr, const void *buffer, + unsigned int count) +{ + outsb(addr, buffer, count); +} +#endif + +#ifndef outsw_p +#define outsw_p outsw_p +static inline void outsw_p(unsigned long addr, const void *buffer, + unsigned int count) +{ + outsw(addr, buffer, count); +} +#endif + +#ifndef outsl_p +#define outsl_p outsl_p +static inline void outsl_p(unsigned long addr, const void *buffer, + unsigned int count) +{ + outsl(addr, buffer, count); +} +#endif + +#ifndef CONFIG_GENERIC_IOMAP +#ifndef ioread8 +#define ioread8 ioread8 +static inline u8 ioread8(const volatile void __iomem *addr) +{ + return readb(addr); +} +#endif + +#ifndef ioread16 +#define ioread16 ioread16 +static inline u16 ioread16(const volatile void __iomem *addr) +{ + return readw(addr); +} +#endif + +#ifndef ioread32 +#define ioread32 ioread32 +static inline u32 ioread32(const volatile void __iomem *addr) +{ + return readl(addr); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef ioread64 +#define ioread64 ioread64 +static inline u64 ioread64(const volatile void __iomem *addr) +{ + return readq(addr); +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef iowrite8 +#define iowrite8 iowrite8 +static inline void iowrite8(u8 value, volatile void __iomem *addr) +{ + writeb(value, addr); +} +#endif + +#ifndef iowrite16 +#define iowrite16 iowrite16 +static inline void iowrite16(u16 value, volatile void __iomem *addr) +{ + writew(value, addr); +} +#endif + +#ifndef iowrite32 +#define iowrite32 iowrite32 +static inline void iowrite32(u32 value, volatile void __iomem *addr) +{ + writel(value, addr); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef iowrite64 +#define iowrite64 iowrite64 +static inline void iowrite64(u64 value, volatile void __iomem *addr) +{ + writeq(value, addr); +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef ioread16be +#define ioread16be ioread16be +static inline u16 ioread16be(const volatile void __iomem *addr) +{ + return swab16(readw(addr)); +} +#endif + +#ifndef ioread32be +#define ioread32be ioread32be +static inline u32 ioread32be(const volatile void __iomem *addr) +{ + return swab32(readl(addr)); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef ioread64be +#define ioread64be ioread64be +static inline u64 ioread64be(const volatile void __iomem *addr) +{ + return swab64(readq(addr)); +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef iowrite16be +#define iowrite16be iowrite16be +static inline void iowrite16be(u16 value, void volatile __iomem *addr) +{ + writew(swab16(value), addr); +} +#endif + +#ifndef iowrite32be +#define iowrite32be iowrite32be +static inline void iowrite32be(u32 value, volatile void __iomem *addr) +{ + writel(swab32(value), addr); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef iowrite64be +#define iowrite64be iowrite64be +static inline void iowrite64be(u64 value, volatile void __iomem *addr) +{ + writeq(swab64(value), addr); +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef ioread8_rep +#define ioread8_rep ioread8_rep +static inline void ioread8_rep(const volatile void __iomem *addr, void *buffer, + unsigned int count) +{ + readsb(addr, buffer, count); +} +#endif + +#ifndef ioread16_rep +#define ioread16_rep ioread16_rep +static inline void ioread16_rep(const volatile void __iomem *addr, + void *buffer, unsigned int count) +{ + readsw(addr, buffer, count); +} +#endif + +#ifndef ioread32_rep +#define ioread32_rep ioread32_rep +static inline void ioread32_rep(const volatile void __iomem *addr, + void *buffer, unsigned int count) +{ + readsl(addr, buffer, count); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef ioread64_rep +#define ioread64_rep ioread64_rep +static inline void ioread64_rep(const volatile void __iomem *addr, + void *buffer, unsigned int count) +{ + readsq(addr, buffer, count); +} +#endif +#endif /* CONFIG_64BIT */ + +#ifndef iowrite8_rep +#define iowrite8_rep iowrite8_rep +static inline void iowrite8_rep(volatile void __iomem *addr, + const void *buffer, + unsigned int count) +{ + writesb(addr, buffer, count); +} +#endif + +#ifndef iowrite16_rep +#define iowrite16_rep iowrite16_rep +static inline void iowrite16_rep(volatile void __iomem *addr, + const void *buffer, + unsigned int count) +{ + writesw(addr, buffer, count); +} +#endif + +#ifndef iowrite32_rep +#define iowrite32_rep iowrite32_rep +static inline void iowrite32_rep(volatile void __iomem *addr, + const void *buffer, + unsigned int count) +{ + writesl(addr, buffer, count); +} +#endif + +#ifdef CONFIG_64BIT +#ifndef iowrite64_rep +#define iowrite64_rep iowrite64_rep +static inline void iowrite64_rep(volatile void __iomem *addr, + const void *buffer, + unsigned int count) +{ + writesq(addr, buffer, count); +} +#endif +#endif /* CONFIG_64BIT */ +#endif /* CONFIG_GENERIC_IOMAP */ + +#ifdef __KERNEL__ + +#include +#define __io_virt(x) ((void __force *)(x)) + +#ifndef CONFIG_GENERIC_IOMAP +struct pci_dev; +extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); + +#ifndef pci_iounmap +#define pci_iounmap pci_iounmap +static inline void pci_iounmap(struct pci_dev *dev, void __iomem *p) +{ +} +#endif +#endif /* CONFIG_GENERIC_IOMAP */ + +/* + * Change virtual addresses to physical addresses and vv. + * These are pretty trivial + */ +#ifndef virt_to_phys +#define virt_to_phys virt_to_phys +static inline unsigned long virt_to_phys(volatile void *address) +{ + return __pa((unsigned long)address); +} +#endif + +#ifndef phys_to_virt +#define phys_to_virt phys_to_virt +static inline void *phys_to_virt(unsigned long address) +{ + return __va(address); +} +#endif + +/** + * DOC: ioremap() and ioremap_*() variants + * + * If you have an IOMMU your architecture is expected to have both ioremap() + * and iounmap() implemented otherwise the asm-generic helpers will provide a + * direct mapping. + * + * There are ioremap_*() call variants, if you have no IOMMU we naturally will + * default to direct mapping for all of them, you can override these defaults. + * If you have an IOMMU you are highly encouraged to provide your own + * ioremap variant implementation as there currently is no safe architecture + * agnostic default. To avoid possible improper behaviour default asm-generic + * ioremap_*() variants all return NULL when an IOMMU is available. If you've + * defined your own ioremap_*() variant you must then declare your own + * ioremap_*() variant as defined to itself to avoid the default NULL return. + */ + +#ifdef CONFIG_MMU + +#ifndef ioremap_uc +#define ioremap_uc ioremap_uc +static inline void __iomem *ioremap_uc(phys_addr_t offset, size_t size) +{ + return NULL; +} +#endif + +#else /* !CONFIG_MMU */ + +/* + * Change "struct page" to physical address. + * + * This implementation is for the no-MMU case only... if you have an MMU + * you'll need to provide your own definitions. + */ + +#ifndef ioremap +#define ioremap ioremap +static inline void __iomem *ioremap(phys_addr_t offset, size_t size) +{ + return (void __iomem *)(unsigned long)offset; +} +#endif + +#ifndef iounmap +#define iounmap iounmap + +static inline void iounmap(void __iomem *addr) +{ +} +#endif +#endif /* CONFIG_MMU */ +#ifndef ioremap_nocache +void __iomem *ioremap(phys_addr_t phys_addr, size_t size); +#define ioremap_nocache ioremap_nocache +static inline void __iomem *ioremap_nocache(phys_addr_t offset, size_t size) +{ + return ioremap(offset, size); +} +#endif + +#ifndef ioremap_uc +#define ioremap_uc ioremap_uc +static inline void __iomem *ioremap_uc(phys_addr_t offset, size_t size) +{ + return ioremap_nocache(offset, size); +} +#endif + +#ifndef ioremap_wc +#define ioremap_wc ioremap_wc +static inline void __iomem *ioremap_wc(phys_addr_t offset, size_t size) +{ + return ioremap_nocache(offset, size); +} +#endif + +#ifndef ioremap_wt +#define ioremap_wt ioremap_wt +static inline void __iomem *ioremap_wt(phys_addr_t offset, size_t size) +{ + return ioremap_nocache(offset, size); +} +#endif + +#ifdef CONFIG_HAS_IOPORT_MAP +#ifndef CONFIG_GENERIC_IOMAP +#ifndef ioport_map +#define ioport_map ioport_map +static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) +{ + port &= IO_SPACE_LIMIT; + return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port; +} +#endif + +#ifndef ioport_unmap +#define ioport_unmap ioport_unmap +static inline void ioport_unmap(void __iomem *p) +{ +} +#endif +#else /* CONFIG_GENERIC_IOMAP */ +extern void __iomem *ioport_map(unsigned long port, unsigned int nr); +extern void ioport_unmap(void __iomem *p); +#endif /* CONFIG_GENERIC_IOMAP */ +#endif /* CONFIG_HAS_IOPORT_MAP */ + +/* + * Convert a virtual cached pointer to an uncached pointer + */ +#ifndef xlate_dev_kmem_ptr +#define xlate_dev_kmem_ptr xlate_dev_kmem_ptr +static inline void *xlate_dev_kmem_ptr(void *addr) +{ + return addr; +} +#endif + +#ifndef xlate_dev_mem_ptr +#define xlate_dev_mem_ptr xlate_dev_mem_ptr +static inline void *xlate_dev_mem_ptr(phys_addr_t addr) +{ + return __va(addr); +} +#endif + +#ifndef unxlate_dev_mem_ptr +#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr +static inline void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr) +{ +} +#endif + +#ifdef CONFIG_VIRT_TO_BUS +#ifndef virt_to_bus +static inline unsigned long virt_to_bus(void *address) +{ + return (unsigned long)address; +} + +static inline void *bus_to_virt(unsigned long address) +{ + return (void *)address; +} +#endif +#endif + +#ifndef memset_io +#define memset_io memset_io +/** + * memset_io Set a range of I/O memory to a constant value + * @addr: The beginning of the I/O-memory range to set + * @val: The value to set the memory to + * @count: The number of bytes to set + * + * Set a range of I/O memory to a given value. + */ +static inline void memset_io(volatile void __iomem *addr, int value, + size_t size) +{ + memset(__io_virt(addr), value, size); +} +#endif + +#ifndef memcpy_fromio +#define memcpy_fromio memcpy_fromio +/** + * memcpy_fromio Copy a block of data from I/O memory + * @dst: The (RAM) destination for the copy + * @src: The (I/O memory) source for the data + * @count: The number of bytes to copy + * + * Copy a block of data from I/O memory. + */ +static inline void memcpy_fromio(void *buffer, + const volatile void __iomem *addr, + size_t size) +{ + memcpy(buffer, __io_virt(addr), size); +} +#endif + +#ifndef memcpy_toio +#define memcpy_toio memcpy_toio +/** + * memcpy_toio Copy a block of data into I/O memory + * @dst: The (I/O memory) destination for the copy + * @src: The (RAM) source for the data + * @count: The number of bytes to copy + * + * Copy a block of data to I/O memory. + */ +static inline void memcpy_toio(volatile void __iomem *addr, const void *buffer, + size_t size) +{ + memcpy(__io_virt(addr), buffer, size); +} +#endif + +#endif /* __KERNEL__ */ + +#endif /* __ASM_GENERIC_IO_H */ diff --git a/t/tree/include/asm-generic/pci_iomap.h b/t/tree/include/asm-generic/pci_iomap.h new file mode 100644 index 00000000..d4f16dcc --- /dev/null +++ b/t/tree/include/asm-generic/pci_iomap.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Generic I/O port emulation. + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#ifndef __ASM_GENERIC_PCI_IOMAP_H +#define __ASM_GENERIC_PCI_IOMAP_H + +struct pci_dev; +#ifdef CONFIG_PCI +/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ +extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); +extern void __iomem *pci_iomap_wc(struct pci_dev *dev, int bar, unsigned long max); +extern void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, + unsigned long offset, + unsigned long maxlen); +extern void __iomem *pci_iomap_wc_range(struct pci_dev *dev, int bar, + unsigned long offset, + unsigned long maxlen); +/* Create a virtual mapping cookie for a port on a given PCI device. + * Do not call this directly, it exists to make it easier for architectures + * to override */ +#ifdef CONFIG_NO_GENERIC_PCI_IOPORT_MAP +extern void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port, + unsigned int nr); +#else +#define __pci_ioport_map(dev, port, nr) ioport_map((port), (nr)) +#endif + +#elif defined(CONFIG_GENERIC_PCI_IOMAP) +static inline void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ + return NULL; +} + +static inline void __iomem *pci_iomap_wc(struct pci_dev *dev, int bar, unsigned long max) +{ + return NULL; +} +static inline void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, + unsigned long offset, + unsigned long maxlen) +{ + return NULL; +} +static inline void __iomem *pci_iomap_wc_range(struct pci_dev *dev, int bar, + unsigned long offset, + unsigned long maxlen) +{ + return NULL; +} +#endif + +#endif /* __ASM_GENERIC_IO_H */ diff --git a/t/tree/include/asm-generic/qrwlock.h b/t/tree/include/asm-generic/qrwlock.h new file mode 100644 index 00000000..3aefde23 --- /dev/null +++ b/t/tree/include/asm-generic/qrwlock.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Queue read/write lock + * + * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P. + * + * Authors: Waiman Long + */ +#ifndef __ASM_GENERIC_QRWLOCK_H +#define __ASM_GENERIC_QRWLOCK_H + +#include +#include +#include + +#include + +/* + * Writer states & reader shift and bias. + */ +#define _QW_WAITING 0x100 /* A writer is waiting */ +#define _QW_LOCKED 0x0ff /* A writer holds the lock */ +#define _QW_WMASK 0x1ff /* Writer mask */ +#define _QR_SHIFT 9 /* Reader count shift */ +#define _QR_BIAS (1U << _QR_SHIFT) + +/* + * External function declarations + */ +extern void queued_read_lock_slowpath(struct qrwlock *lock); +extern void queued_write_lock_slowpath(struct qrwlock *lock); + +/** + * queued_read_trylock - try to acquire read lock of a queue rwlock + * @lock : Pointer to queue rwlock structure + * Return: 1 if lock acquired, 0 if failed + */ +static inline int queued_read_trylock(struct qrwlock *lock) +{ + u32 cnts; + + cnts = atomic_read(&lock->cnts); + if (likely(!(cnts & _QW_WMASK))) { + cnts = (u32)atomic_add_return_acquire(_QR_BIAS, &lock->cnts); + if (likely(!(cnts & _QW_WMASK))) + return 1; + atomic_sub(_QR_BIAS, &lock->cnts); + } + return 0; +} + +/** + * queued_write_trylock - try to acquire write lock of a queue rwlock + * @lock : Pointer to queue rwlock structure + * Return: 1 if lock acquired, 0 if failed + */ +static inline int queued_write_trylock(struct qrwlock *lock) +{ + u32 cnts; + + cnts = atomic_read(&lock->cnts); + if (unlikely(cnts)) + return 0; + + return likely(atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, + _QW_LOCKED)); +} +/** + * queued_read_lock - acquire read lock of a queue rwlock + * @lock: Pointer to queue rwlock structure + */ +static inline void queued_read_lock(struct qrwlock *lock) +{ + u32 cnts; + + cnts = atomic_add_return_acquire(_QR_BIAS, &lock->cnts); + if (likely(!(cnts & _QW_WMASK))) + return; + + /* The slowpath will decrement the reader count, if necessary. */ + queued_read_lock_slowpath(lock); +} + +/** + * queued_write_lock - acquire write lock of a queue rwlock + * @lock : Pointer to queue rwlock structure + */ +static inline void queued_write_lock(struct qrwlock *lock) +{ + u32 cnts = 0; + /* Optimize for the unfair lock case where the fair flag is 0. */ + if (likely(atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, _QW_LOCKED))) + return; + + queued_write_lock_slowpath(lock); +} + +/** + * queued_read_unlock - release read lock of a queue rwlock + * @lock : Pointer to queue rwlock structure + */ +static inline void queued_read_unlock(struct qrwlock *lock) +{ + /* + * Atomically decrement the reader count + */ + (void)atomic_sub_return_release(_QR_BIAS, &lock->cnts); +} + +/** + * queued_write_unlock - release write lock of a queue rwlock + * @lock : Pointer to queue rwlock structure + */ +static inline void queued_write_unlock(struct qrwlock *lock) +{ + smp_store_release(&lock->wlocked, 0); +} + +/* + * Remapping rwlock architecture specific functions to the corresponding + * queue rwlock functions. + */ +#define arch_read_lock(l) queued_read_lock(l) +#define arch_write_lock(l) queued_write_lock(l) +#define arch_read_trylock(l) queued_read_trylock(l) +#define arch_write_trylock(l) queued_write_trylock(l) +#define arch_read_unlock(l) queued_read_unlock(l) +#define arch_write_unlock(l) queued_write_unlock(l) + +#endif /* __ASM_GENERIC_QRWLOCK_H */ diff --git a/t/tree/include/asm-generic/qspinlock.h b/t/tree/include/asm-generic/qspinlock.h new file mode 100644 index 00000000..fde943d1 --- /dev/null +++ b/t/tree/include/asm-generic/qspinlock.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Queued spinlock + * + * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2015 Hewlett-Packard Enterprise Development LP + * + * Authors: Waiman Long + */ +#ifndef __ASM_GENERIC_QSPINLOCK_H +#define __ASM_GENERIC_QSPINLOCK_H + +#include + +/** + * queued_spin_is_locked - is the spinlock locked? + * @lock: Pointer to queued spinlock structure + * Return: 1 if it is locked, 0 otherwise + */ +static __always_inline int queued_spin_is_locked(struct qspinlock *lock) +{ + /* + * Any !0 state indicates it is locked, even if _Q_LOCKED_VAL + * isn't immediately observable. + */ + return atomic_read(&lock->val); +} + +/** + * queued_spin_value_unlocked - is the spinlock structure unlocked? + * @lock: queued spinlock structure + * Return: 1 if it is unlocked, 0 otherwise + * + * N.B. Whenever there are tasks waiting for the lock, it is considered + * locked wrt the lockref code to avoid lock stealing by the lockref + * code and change things underneath the lock. This also allows some + * optimizations to be applied without conflict with lockref. + */ +static __always_inline int queued_spin_value_unlocked(struct qspinlock lock) +{ + return !atomic_read(&lock.val); +} + +/** + * queued_spin_is_contended - check if the lock is contended + * @lock : Pointer to queued spinlock structure + * Return: 1 if lock contended, 0 otherwise + */ +static __always_inline int queued_spin_is_contended(struct qspinlock *lock) +{ + return atomic_read(&lock->val) & ~_Q_LOCKED_MASK; +} +/** + * queued_spin_trylock - try to acquire the queued spinlock + * @lock : Pointer to queued spinlock structure + * Return: 1 if lock acquired, 0 if failed + */ +static __always_inline int queued_spin_trylock(struct qspinlock *lock) +{ + u32 val = atomic_read(&lock->val); + + if (unlikely(val)) + return 0; + + return likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)); +} + +extern void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); + +/** + * queued_spin_lock - acquire a queued spinlock + * @lock: Pointer to queued spinlock structure + */ +static __always_inline void queued_spin_lock(struct qspinlock *lock) +{ + u32 val = 0; + + if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL))) + return; + + queued_spin_lock_slowpath(lock, val); +} + +#ifndef queued_spin_unlock +/** + * queued_spin_unlock - release a queued spinlock + * @lock : Pointer to queued spinlock structure + */ +static __always_inline void queued_spin_unlock(struct qspinlock *lock) +{ + /* + * unlock() needs release semantics: + */ + smp_store_release(&lock->locked, 0); +} +#endif + +#ifndef virt_spin_lock +static __always_inline bool virt_spin_lock(struct qspinlock *lock) +{ + return false; +} +#endif + +/* + * Remapping spinlock architecture specific functions to the corresponding + * queued spinlock functions. + */ +#define arch_spin_is_locked(l) queued_spin_is_locked(l) +#define arch_spin_is_contended(l) queued_spin_is_contended(l) +#define arch_spin_value_unlocked(l) queued_spin_value_unlocked(l) +#define arch_spin_lock(l) queued_spin_lock(l) +#define arch_spin_trylock(l) queued_spin_trylock(l) +#define arch_spin_unlock(l) queued_spin_unlock(l) + +#endif /* __ASM_GENERIC_QSPINLOCK_H */ diff --git a/t/tree/include/asm-generic/qspinlock_types.h b/t/tree/include/asm-generic/qspinlock_types.h new file mode 100644 index 00000000..56d1309d --- /dev/null +++ b/t/tree/include/asm-generic/qspinlock_types.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Queued spinlock + * + * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P. + * + * Authors: Waiman Long + */ +#ifndef __ASM_GENERIC_QSPINLOCK_TYPES_H +#define __ASM_GENERIC_QSPINLOCK_TYPES_H + +/* + * Including atomic.h with PARAVIRT on will cause compilation errors because + * of recursive header file incluson via paravirt_types.h. So don't include + * it if PARAVIRT is on. + */ +#ifndef CONFIG_PARAVIRT +#include +#include +#endif + +typedef struct qspinlock { + union { + atomic_t val; + + /* + * By using the whole 2nd least significant byte for the + * pending bit, we can allow better optimization of the lock + * acquisition for the pending bit holder. + */ +#ifdef __LITTLE_ENDIAN + struct { + u8 locked; + u8 pending; + }; + struct { + u16 locked_pending; + u16 tail; + }; +#else + struct { + u16 tail; + u16 locked_pending; + }; + struct { + u8 reserved[2]; + u8 pending; + u8 locked; + }; +#endif + }; +} arch_spinlock_t; + +/* + * Initializier + */ +#define __ARCH_SPIN_LOCK_UNLOCKED { { .val = ATOMIC_INIT(0) } } + +/* + * Bitfields in the atomic value: + * + * When NR_CPUS < 16K + * 0- 7: locked byte + * 8: pending + * 9-15: not used + * 16-17: tail index + * 18-31: tail cpu (+1) + * + * When NR_CPUS >= 16K + * 0- 7: locked byte + * 8: pending + * 9-10: tail index + * 11-31: tail cpu (+1) + */ +#define _Q_SET_MASK(type) (((1U << _Q_ ## type ## _BITS) - 1)\ + << _Q_ ## type ## _OFFSET) +#define _Q_LOCKED_OFFSET 0 +#define _Q_LOCKED_BITS 8 +#define _Q_LOCKED_MASK _Q_SET_MASK(LOCKED) + +#define _Q_PENDING_OFFSET (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS) +#if CONFIG_NR_CPUS < (1U << 14) +#define _Q_PENDING_BITS 8 +#else +#define _Q_PENDING_BITS 1 +#endif +#define _Q_PENDING_MASK _Q_SET_MASK(PENDING) + +#define _Q_TAIL_IDX_OFFSET (_Q_PENDING_OFFSET + _Q_PENDING_BITS) +#define _Q_TAIL_IDX_BITS 2 +#define _Q_TAIL_IDX_MASK _Q_SET_MASK(TAIL_IDX) + +#define _Q_TAIL_CPU_OFFSET (_Q_TAIL_IDX_OFFSET + _Q_TAIL_IDX_BITS) +#define _Q_TAIL_CPU_BITS (32 - _Q_TAIL_CPU_OFFSET) +#define _Q_TAIL_CPU_MASK _Q_SET_MASK(TAIL_CPU) + +#define _Q_TAIL_OFFSET _Q_TAIL_IDX_OFFSET +#define _Q_TAIL_MASK (_Q_TAIL_IDX_MASK | _Q_TAIL_CPU_MASK) + +#define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET) +#define _Q_PENDING_VAL (1U << _Q_PENDING_OFFSET) + +#endif /* __ASM_GENERIC_QSPINLOCK_TYPES_H */ diff --git a/t/tree/include/linux/acpi.h b/t/tree/include/linux/acpi.h new file mode 100644 index 00000000..8b4e516b --- /dev/null +++ b/t/tree/include/linux/acpi.h @@ -0,0 +1,1302 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * acpi.h - ACPI Interface + * + * Copyright (C) 2001 Paul Diefenbaugh + */ + +#ifndef _LINUX_ACPI_H +#define _LINUX_ACPI_H + +#include +#include /* for struct resource */ +#include +#include +#include +#include +#include + +#ifndef _LINUX +#define _LINUX +#endif +#include + +#ifdef CONFIG_ACPI + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static inline acpi_handle acpi_device_handle(struct acpi_device *adev) +{ + return adev ? adev->handle : NULL; +} + +#define ACPI_COMPANION(dev) to_acpi_device_node((dev)->fwnode) +#define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ + acpi_fwnode_handle(adev) : NULL) +#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) +#define ACPI_HANDLE_FWNODE(fwnode) \ + acpi_device_handle(to_acpi_device_node(fwnode)) + +static inline struct fwnode_handle *acpi_alloc_fwnode_static(void) +{ + struct fwnode_handle *fwnode; + + fwnode = kzalloc(sizeof(struct fwnode_handle), GFP_KERNEL); + if (!fwnode) + return NULL; + + fwnode->ops = &acpi_static_fwnode_ops; + + return fwnode; +} + +static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode) +{ + if (WARN_ON(!is_acpi_static_node(fwnode))) + return; + + kfree(fwnode); +} + +/** + * ACPI_DEVICE_CLASS - macro used to describe an ACPI device with + * the PCI-defined class-code information + * + * @_cls : the class, subclass, prog-if triple for this device + * @_msk : the class mask for this device + * + * This macro is used to create a struct acpi_device_id that matches a + * specific PCI class. The .id and .driver_data fields will be left + * initialized with the default value. + */ +#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (_cls), .cls_msk = (_msk), + +static inline bool has_acpi_companion(struct device *dev) +{ + return is_acpi_device_node(dev->fwnode); +} + +static inline void acpi_preset_companion(struct device *dev, + struct acpi_device *parent, u64 addr) +{ + ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, false)); +} + +static inline const char *acpi_dev_name(struct acpi_device *adev) +{ + return dev_name(&adev->dev); +} + +struct device *acpi_get_first_physical_node(struct acpi_device *adev); + +enum acpi_irq_model_id { + ACPI_IRQ_MODEL_PIC = 0, + ACPI_IRQ_MODEL_IOAPIC, + ACPI_IRQ_MODEL_IOSAPIC, + ACPI_IRQ_MODEL_PLATFORM, + ACPI_IRQ_MODEL_GIC, + ACPI_IRQ_MODEL_COUNT +}; + +extern enum acpi_irq_model_id acpi_irq_model; + +enum acpi_interrupt_id { + ACPI_INTERRUPT_PMI = 1, + ACPI_INTERRUPT_INIT, + ACPI_INTERRUPT_CPEI, + ACPI_INTERRUPT_COUNT +}; + +#define ACPI_SPACE_MEM 0 + +enum acpi_address_range_id { + ACPI_ADDRESS_RANGE_MEMORY = 1, + ACPI_ADDRESS_RANGE_RESERVED = 2, + ACPI_ADDRESS_RANGE_ACPI = 3, + ACPI_ADDRESS_RANGE_NVS = 4, + ACPI_ADDRESS_RANGE_COUNT +}; + + +/* Table Handlers */ +union acpi_subtable_headers { + struct acpi_subtable_header common; + struct acpi_hmat_structure hmat; +}; + +typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table); + +typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *header, + const unsigned long end); + +/* Debugger support */ + +struct acpi_debugger_ops { + int (*create_thread)(acpi_osd_exec_callback function, void *context); + ssize_t (*write_log)(const char *msg); + ssize_t (*read_cmd)(char *buffer, size_t length); + int (*wait_command_ready)(bool single_step, char *buffer, size_t length); + int (*notify_command_complete)(void); +}; + +struct acpi_debugger { + const struct acpi_debugger_ops *ops; + struct module *owner; + struct mutex lock; +}; + +#ifdef CONFIG_ACPI_DEBUGGER +int __init acpi_debugger_init(void); +int acpi_register_debugger(struct module *owner, + const struct acpi_debugger_ops *ops); +void acpi_unregister_debugger(const struct acpi_debugger_ops *ops); +int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context); +ssize_t acpi_debugger_write_log(const char *msg); +ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length); +int acpi_debugger_wait_command_ready(void); +int acpi_debugger_notify_command_complete(void); +#else +static inline int acpi_debugger_init(void) +{ + return -ENODEV; +} + +static inline int acpi_register_debugger(struct module *owner, + const struct acpi_debugger_ops *ops) +{ + return -ENODEV; +} + +static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops) +{ +} + +static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function, + void *context) +{ + return -ENODEV; +} + +static inline int acpi_debugger_write_log(const char *msg) +{ + return -ENODEV; +} + +static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length) +{ + return -ENODEV; +} + +static inline int acpi_debugger_wait_command_ready(void) +{ + return -ENODEV; +} + +static inline int acpi_debugger_notify_command_complete(void) +{ + return -ENODEV; +} +#endif + +#define BAD_MADT_ENTRY(entry, end) ( \ + (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ + ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) + +struct acpi_subtable_proc { + int id; + acpi_tbl_entry_handler handler; + int count; +}; + +void __iomem *__acpi_map_table(unsigned long phys, unsigned long size); +void __acpi_unmap_table(void __iomem *map, unsigned long size); +int early_acpi_boot_init(void); +int acpi_boot_init (void); +void acpi_boot_table_init (void); +int acpi_mps_check (void); +int acpi_numa_init (void); + +int acpi_table_init (void); +int acpi_table_parse(char *id, acpi_tbl_table_handler handler); +int __init acpi_table_parse_entries(char *id, unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries); +int __init acpi_table_parse_entries_array(char *id, unsigned long table_size, + struct acpi_subtable_proc *proc, int proc_num, + unsigned int max_entries); +int acpi_table_parse_madt(enum acpi_madt_type id, + acpi_tbl_entry_handler handler, + unsigned int max_entries); +int acpi_parse_mcfg (struct acpi_table_header *header); +void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); + +/* the following numa functions are architecture-dependent */ +void acpi_numa_slit_init (struct acpi_table_slit *slit); + +#if defined(CONFIG_X86) || defined(CONFIG_IA64) +void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); +#else +static inline void +acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) { } +#endif + +void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); + +#ifdef CONFIG_ARM64 +void acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa); +#else +static inline void +acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa) { } +#endif + +int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); + +#ifndef PHYS_CPUID_INVALID +typedef u32 phys_cpuid_t; +#define PHYS_CPUID_INVALID (phys_cpuid_t)(-1) +#endif + +static inline bool invalid_logical_cpuid(u32 cpuid) +{ + return (int)cpuid < 0; +} + +static inline bool invalid_phys_cpuid(phys_cpuid_t phys_id) +{ + return phys_id == PHYS_CPUID_INVALID; +} + +/* Validate the processor object's proc_id */ +bool acpi_duplicate_processor_id(int proc_id); + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +/* Arch dependent functions for cpu hotplug support */ +int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, + int *pcpu); +int acpi_unmap_cpu(int cpu); +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr); +#endif + +int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base); +int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base); +int acpi_ioapic_registered(acpi_handle handle, u32 gsi_base); +void acpi_irq_stats_init(void); +extern u32 acpi_irq_handled; +extern u32 acpi_irq_not_handled; +extern unsigned int acpi_sci_irq; +extern bool acpi_no_s5; +#define INVALID_ACPI_IRQ ((unsigned)-1) +static inline bool acpi_sci_irq_valid(void) +{ + return acpi_sci_irq != INVALID_ACPI_IRQ; +} + +extern int sbf_port; +extern unsigned long acpi_realmode_flags; + +int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity); +int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); +int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi); + +void acpi_set_irq_model(enum acpi_irq_model_id model, + struct fwnode_handle *fwnode); + +struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, + unsigned int size, + struct fwnode_handle *fwnode, + const struct irq_domain_ops *ops, + void *host_data); + +#ifdef CONFIG_X86_IO_APIC +extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity); +#else +static inline int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity) +{ + return -1; +} +#endif +/* + * This function undoes the effect of one call to acpi_register_gsi(). + * If this matches the last registration, any IRQ resources for gsi + * are freed. + */ +void acpi_unregister_gsi (u32 gsi); + +struct pci_dev; + +int acpi_pci_irq_enable (struct pci_dev *dev); +void acpi_penalize_isa_irq(int irq, int active); +bool acpi_isa_irq_available(int irq); +#ifdef CONFIG_PCI +void acpi_penalize_sci_irq(int irq, int trigger, int polarity); +#else +static inline void acpi_penalize_sci_irq(int irq, int trigger, + int polarity) +{ +} +#endif +void acpi_pci_irq_disable (struct pci_dev *dev); + +extern int ec_read(u8 addr, u8 *val); +extern int ec_write(u8 addr, u8 val); +extern int ec_transaction(u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len); +extern acpi_handle ec_get_handle(void); + +extern bool acpi_is_pnp_device(struct acpi_device *); + +#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) + +typedef void (*wmi_notify_handler) (u32 value, void *context); + +extern acpi_status wmi_evaluate_method(const char *guid, u8 instance, + u32 method_id, + const struct acpi_buffer *in, + struct acpi_buffer *out); +extern acpi_status wmi_query_block(const char *guid, u8 instance, + struct acpi_buffer *out); +extern acpi_status wmi_set_block(const char *guid, u8 instance, + const struct acpi_buffer *in); +extern acpi_status wmi_install_notify_handler(const char *guid, + wmi_notify_handler handler, void *data); +extern acpi_status wmi_remove_notify_handler(const char *guid); +extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out); +extern bool wmi_has_guid(const char *guid); +extern char *wmi_get_acpi_device_uid(const char *guid); + +#endif /* CONFIG_ACPI_WMI */ + +#define ACPI_VIDEO_OUTPUT_SWITCHING 0x0001 +#define ACPI_VIDEO_DEVICE_POSTING 0x0002 +#define ACPI_VIDEO_ROM_AVAILABLE 0x0004 +#define ACPI_VIDEO_BACKLIGHT 0x0008 +#define ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR 0x0010 +#define ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO 0x0020 +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR 0x0040 +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO 0x0080 +#define ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 0x0100 +#define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200 +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400 +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800 + +extern char acpi_video_backlight_string[]; +extern long acpi_is_video_device(acpi_handle handle); +extern int acpi_blacklisted(void); +extern void acpi_osi_setup(char *str); +extern bool acpi_osi_is_win8(void); + +#ifdef CONFIG_ACPI_NUMA +int acpi_map_pxm_to_online_node(int pxm); +int acpi_map_pxm_to_node(int pxm); +int acpi_get_node(acpi_handle handle); +#else +static inline int acpi_map_pxm_to_online_node(int pxm) +{ + return 0; +} +static inline int acpi_map_pxm_to_node(int pxm) +{ + return 0; +} +static inline int acpi_get_node(acpi_handle handle) +{ + return 0; +} +#endif +extern int acpi_paddr_to_node(u64 start_addr, u64 size); + +extern int pnpacpi_disabled; + +#define PXM_INVAL (-1) + +bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res); +bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res); +bool acpi_dev_resource_address_space(struct acpi_resource *ares, + struct resource_win *win); +bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, + struct resource_win *win); +unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable); +unsigned int acpi_dev_get_irq_type(int triggering, int polarity); +bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, + struct resource *res); + +void acpi_dev_free_resource_list(struct list_head *list); +int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, + int (*preproc)(struct acpi_resource *, void *), + void *preproc_data); +int acpi_dev_get_dma_resources(struct acpi_device *adev, + struct list_head *list); +int acpi_dev_filter_resource_type(struct acpi_resource *ares, + unsigned long types); + +static inline int acpi_dev_filter_resource_type_cb(struct acpi_resource *ares, + void *arg) +{ + return acpi_dev_filter_resource_type(ares, (unsigned long)arg); +} + +struct acpi_device *acpi_resource_consumer(struct resource *res); + +int acpi_check_resource_conflict(const struct resource *res); + +int acpi_check_region(resource_size_t start, resource_size_t n, + const char *name); + +acpi_status acpi_release_memory(acpi_handle handle, struct resource *res, + u32 level); + +int acpi_resources_are_enforced(void); + +#ifdef CONFIG_HIBERNATION +void __init acpi_no_s4_hw_signature(void); +#endif + +#ifdef CONFIG_PM_SLEEP +void __init acpi_old_suspend_ordering(void); +void __init acpi_nvs_nosave(void); +void __init acpi_nvs_nosave_s3(void); +void __init acpi_sleep_no_blacklist(void); +#endif /* CONFIG_PM_SLEEP */ + +struct acpi_osc_context { + char *uuid_str; /* UUID string */ + int rev; + struct acpi_buffer cap; /* list of DWORD capabilities */ + struct acpi_buffer ret; /* free by caller if success */ +}; + +acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); + +/* Indexes into _OSC Capabilities Buffer (DWORDs 2 & 3 are device-specific) */ +#define OSC_QUERY_DWORD 0 /* DWORD 1 */ +#define OSC_SUPPORT_DWORD 1 /* DWORD 2 */ +#define OSC_CONTROL_DWORD 2 /* DWORD 3 */ + +/* _OSC Capabilities DWORD 1: Query/Control and Error Returns (generic) */ +#define OSC_QUERY_ENABLE 0x00000001 /* input */ +#define OSC_REQUEST_ERROR 0x00000002 /* return */ +#define OSC_INVALID_UUID_ERROR 0x00000004 /* return */ +#define OSC_INVALID_REVISION_ERROR 0x00000008 /* return */ +#define OSC_CAPABILITIES_MASK_ERROR 0x00000010 /* return */ + +/* Platform-Wide Capabilities _OSC: Capabilities DWORD 2: Support Field */ +#define OSC_SB_PAD_SUPPORT 0x00000001 +#define OSC_SB_PPC_OST_SUPPORT 0x00000002 +#define OSC_SB_PR3_SUPPORT 0x00000004 +#define OSC_SB_HOTPLUG_OST_SUPPORT 0x00000008 +#define OSC_SB_APEI_SUPPORT 0x00000010 +#define OSC_SB_CPC_SUPPORT 0x00000020 +#define OSC_SB_CPCV2_SUPPORT 0x00000040 +#define OSC_SB_PCLPI_SUPPORT 0x00000080 +#define OSC_SB_OSLPI_SUPPORT 0x00000100 +#define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT 0x00001000 + +extern bool osc_sb_apei_support_acked; +extern bool osc_pc_lpi_support_confirmed; + +/* PCI Host Bridge _OSC: Capabilities DWORD 2: Support Field */ +#define OSC_PCI_EXT_CONFIG_SUPPORT 0x00000001 +#define OSC_PCI_ASPM_SUPPORT 0x00000002 +#define OSC_PCI_CLOCK_PM_SUPPORT 0x00000004 +#define OSC_PCI_SEGMENT_GROUPS_SUPPORT 0x00000008 +#define OSC_PCI_MSI_SUPPORT 0x00000010 +#define OSC_PCI_HPX_TYPE_3_SUPPORT 0x00000100 +#define OSC_PCI_SUPPORT_MASKS 0x0000011f + +/* PCI Host Bridge _OSC: Capabilities DWORD 3: Control Field */ +#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 0x00000001 +#define OSC_PCI_SHPC_NATIVE_HP_CONTROL 0x00000002 +#define OSC_PCI_EXPRESS_PME_CONTROL 0x00000004 +#define OSC_PCI_EXPRESS_AER_CONTROL 0x00000008 +#define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010 +#define OSC_PCI_EXPRESS_LTR_CONTROL 0x00000020 +#define OSC_PCI_CONTROL_MASKS 0x0000003f + +#define ACPI_GSB_ACCESS_ATTRIB_QUICK 0x00000002 +#define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV 0x00000004 +#define ACPI_GSB_ACCESS_ATTRIB_BYTE 0x00000006 +#define ACPI_GSB_ACCESS_ATTRIB_WORD 0x00000008 +#define ACPI_GSB_ACCESS_ATTRIB_BLOCK 0x0000000A +#define ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE 0x0000000B +#define ACPI_GSB_ACCESS_ATTRIB_WORD_CALL 0x0000000C +#define ACPI_GSB_ACCESS_ATTRIB_BLOCK_CALL 0x0000000D +#define ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES 0x0000000E +#define ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS 0x0000000F + +extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, + u32 *mask, u32 req); + +/* Enable _OST when all relevant hotplug operations are enabled */ +#if defined(CONFIG_ACPI_HOTPLUG_CPU) && \ + defined(CONFIG_ACPI_HOTPLUG_MEMORY) && \ + defined(CONFIG_ACPI_CONTAINER) +#define ACPI_HOTPLUG_OST +#endif + +/* _OST Source Event Code (OSPM Action) */ +#define ACPI_OST_EC_OSPM_SHUTDOWN 0x100 +#define ACPI_OST_EC_OSPM_EJECT 0x103 +#define ACPI_OST_EC_OSPM_INSERTION 0x200 + +/* _OST General Processing Status Code */ +#define ACPI_OST_SC_SUCCESS 0x0 +#define ACPI_OST_SC_NON_SPECIFIC_FAILURE 0x1 +#define ACPI_OST_SC_UNRECOGNIZED_NOTIFY 0x2 + +/* _OST OS Shutdown Processing (0x100) Status Code */ +#define ACPI_OST_SC_OS_SHUTDOWN_DENIED 0x80 +#define ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS 0x81 +#define ACPI_OST_SC_OS_SHUTDOWN_COMPLETED 0x82 +#define ACPI_OST_SC_OS_SHUTDOWN_NOT_SUPPORTED 0x83 + +/* _OST Ejection Request (0x3, 0x103) Status Code */ +#define ACPI_OST_SC_EJECT_NOT_SUPPORTED 0x80 +#define ACPI_OST_SC_DEVICE_IN_USE 0x81 +#define ACPI_OST_SC_DEVICE_BUSY 0x82 +#define ACPI_OST_SC_EJECT_DEPENDENCY_BUSY 0x83 +#define ACPI_OST_SC_EJECT_IN_PROGRESS 0x84 + +/* _OST Insertion Request (0x200) Status Code */ +#define ACPI_OST_SC_INSERT_IN_PROGRESS 0x80 +#define ACPI_OST_SC_DRIVER_LOAD_FAILURE 0x81 +#define ACPI_OST_SC_INSERT_NOT_SUPPORTED 0x82 + +enum acpi_predicate { + all_versions, + less_than_or_equal, + equal, + greater_than_or_equal, +}; + +/* Table must be terminted by a NULL entry */ +struct acpi_platform_list { + char oem_id[ACPI_OEM_ID_SIZE+1]; + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE+1]; + u32 oem_revision; + char *table; + enum acpi_predicate pred; + char *reason; + u32 data; +}; +int acpi_match_platform_list(const struct acpi_platform_list *plat); + +extern void acpi_early_init(void); +extern void acpi_subsystem_init(void); +extern void arch_post_acpi_subsys_init(void); + +extern int acpi_nvs_register(__u64 start, __u64 size); + +extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), + void *data); + +const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, + const struct device *dev); + +const void *acpi_device_get_match_data(const struct device *dev); +extern bool acpi_driver_match_device(struct device *dev, + const struct device_driver *drv); +int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); +int acpi_device_modalias(struct device *, char *, int); +void acpi_walk_dep_device_list(acpi_handle handle); + +struct platform_device *acpi_create_platform_device(struct acpi_device *, + struct property_entry *); +#define ACPI_PTR(_ptr) (_ptr) + +static inline void acpi_device_set_enumerated(struct acpi_device *adev) +{ + adev->flags.visited = true; +} + +static inline void acpi_device_clear_enumerated(struct acpi_device *adev) +{ + adev->flags.visited = false; +} + +enum acpi_reconfig_event { + ACPI_RECONFIG_DEVICE_ADD = 0, + ACPI_RECONFIG_DEVICE_REMOVE, +}; + +int acpi_reconfig_notifier_register(struct notifier_block *nb); +int acpi_reconfig_notifier_unregister(struct notifier_block *nb); + +#ifdef CONFIG_ACPI_GTDT +int acpi_gtdt_init(struct acpi_table_header *table, int *platform_timer_count); +int acpi_gtdt_map_ppi(int type); +bool acpi_gtdt_c3stop(int type); +int acpi_arch_timer_mem_init(struct arch_timer_mem *timer_mem, int *timer_count); +#endif + +#ifndef ACPI_HAVE_ARCH_SET_ROOT_POINTER +static inline void acpi_arch_set_root_pointer(u64 addr) +{ +} +#endif + +#ifndef ACPI_HAVE_ARCH_GET_ROOT_POINTER +static inline u64 acpi_arch_get_root_pointer(void) +{ + return 0; +} +#endif + +#else /* !CONFIG_ACPI */ + +#define acpi_disabled 1 + +#define ACPI_COMPANION(dev) (NULL) +#define ACPI_COMPANION_SET(dev, adev) do { } while (0) +#define ACPI_HANDLE(dev) (NULL) +#define ACPI_HANDLE_FWNODE(fwnode) (NULL) +#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (0), .cls_msk = (0), + +struct fwnode_handle; + +static inline bool acpi_dev_found(const char *hid) +{ + return false; +} + +static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv) +{ + return false; +} + +static inline struct acpi_device * +acpi_dev_get_first_match_dev(const char *hid, const char *uid, s64 hrv) +{ + return NULL; +} + +static inline void acpi_dev_put(struct acpi_device *adev) {} + +static inline bool is_acpi_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline bool is_acpi_device_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct acpi_device *to_acpi_device_node(struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline bool is_acpi_data_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline bool acpi_data_node_match(struct fwnode_handle *fwnode, + const char *name) +{ + return false; +} + +static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) +{ + return NULL; +} + +static inline bool has_acpi_companion(struct device *dev) +{ + return false; +} + +static inline void acpi_preset_companion(struct device *dev, + struct acpi_device *parent, u64 addr) +{ +} + +static inline const char *acpi_dev_name(struct acpi_device *adev) +{ + return NULL; +} + +static inline struct device *acpi_get_first_physical_node(struct acpi_device *adev) +{ + return NULL; +} + +static inline void acpi_early_init(void) { } +static inline void acpi_subsystem_init(void) { } + +static inline int early_acpi_boot_init(void) +{ + return 0; +} +static inline int acpi_boot_init(void) +{ + return 0; +} + +static inline void acpi_boot_table_init(void) +{ + return; +} + +static inline int acpi_mps_check(void) +{ + return 0; +} + +static inline int acpi_check_resource_conflict(struct resource *res) +{ + return 0; +} + +static inline int acpi_check_region(resource_size_t start, resource_size_t n, + const char *name) +{ + return 0; +} + +struct acpi_table_header; +static inline int acpi_table_parse(char *id, + int (*handler)(struct acpi_table_header *)) +{ + return -ENODEV; +} + +static inline int acpi_nvs_register(__u64 start, __u64 size) +{ + return 0; +} + +static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), + void *data) +{ + return 0; +} + +struct acpi_device_id; + +static inline const struct acpi_device_id *acpi_match_device( + const struct acpi_device_id *ids, const struct device *dev) +{ + return NULL; +} + +static inline const void *acpi_device_get_match_data(const struct device *dev) +{ + return NULL; +} + +static inline bool acpi_driver_match_device(struct device *dev, + const struct device_driver *drv) +{ + return false; +} + +static inline union acpi_object *acpi_evaluate_dsm(acpi_handle handle, + const guid_t *guid, + int rev, int func, + union acpi_object *argv4) +{ + return NULL; +} + +static inline int acpi_device_uevent_modalias(struct device *dev, + struct kobj_uevent_env *env) +{ + return -ENODEV; +} + +static inline int acpi_device_modalias(struct device *dev, + char *buf, int size) +{ + return -ENODEV; +} + +static inline bool acpi_dma_supported(struct acpi_device *adev) +{ + return false; +} + +static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev) +{ + return DEV_DMA_NOT_SUPPORTED; +} + +static inline int acpi_dma_get_range(struct device *dev, u64 *dma_addr, + u64 *offset, u64 *size) +{ + return -ENODEV; +} + +static inline int acpi_dma_configure(struct device *dev, + enum dev_dma_attr attr) +{ + return 0; +} + +#define ACPI_PTR(_ptr) (NULL) + +static inline void acpi_device_set_enumerated(struct acpi_device *adev) +{ +} + +static inline void acpi_device_clear_enumerated(struct acpi_device *adev) +{ +} + +static inline int acpi_reconfig_notifier_register(struct notifier_block *nb) +{ + return -EINVAL; +} + +static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return -EINVAL; +} + +static inline struct acpi_device *acpi_resource_consumer(struct resource *res) +{ + return NULL; +} + +#endif /* !CONFIG_ACPI */ + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_ioapic_add(acpi_handle root); +#else +static inline int acpi_ioapic_add(acpi_handle root) { return 0; } +#endif + +#ifdef CONFIG_ACPI +void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, + u32 pm1a_ctrl, u32 pm1b_ctrl)); + +acpi_status acpi_os_prepare_sleep(u8 sleep_state, + u32 pm1a_control, u32 pm1b_control); + +void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, + u32 val_a, u32 val_b)); + +acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, + u32 val_a, u32 val_b); + +#ifdef CONFIG_X86 +void arch_reserve_mem_area(acpi_physical_address addr, size_t size); +#else +static inline void arch_reserve_mem_area(acpi_physical_address addr, + size_t size) +{ +} +#endif /* CONFIG_X86 */ +#else +#define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) +#endif + +#if defined(CONFIG_ACPI) && defined(CONFIG_PM) +int acpi_dev_suspend(struct device *dev, bool wakeup); +int acpi_dev_resume(struct device *dev); +int acpi_subsys_runtime_suspend(struct device *dev); +int acpi_subsys_runtime_resume(struct device *dev); +int acpi_dev_pm_attach(struct device *dev, bool power_on); +#else +static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; } +static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; } +static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } +static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } +static inline int acpi_dev_pm_attach(struct device *dev, bool power_on) +{ + return 0; +} +#endif + +#if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP) +int acpi_subsys_prepare(struct device *dev); +void acpi_subsys_complete(struct device *dev); +int acpi_subsys_suspend_late(struct device *dev); +int acpi_subsys_suspend_noirq(struct device *dev); +int acpi_subsys_suspend(struct device *dev); +int acpi_subsys_freeze(struct device *dev); +int acpi_subsys_poweroff(struct device *dev); +void acpi_ec_mark_gpe_for_wake(void); +void acpi_ec_set_gpe_wake_mask(u8 action); +#else +static inline int acpi_subsys_prepare(struct device *dev) { return 0; } +static inline void acpi_subsys_complete(struct device *dev) {} +static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } +static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; } +static inline int acpi_subsys_suspend(struct device *dev) { return 0; } +static inline int acpi_subsys_freeze(struct device *dev) { return 0; } +static inline int acpi_subsys_poweroff(struct device *dev) { return 0; } +static inline void acpi_ec_mark_gpe_for_wake(void) {} +static inline void acpi_ec_set_gpe_wake_mask(u8 action) {} +#endif + +#ifdef CONFIG_ACPI +__printf(3, 4) +void acpi_handle_printk(const char *level, acpi_handle handle, + const char *fmt, ...); +#else /* !CONFIG_ACPI */ +static inline __printf(3, 4) void +acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {} +#endif /* !CONFIG_ACPI */ + +#if defined(CONFIG_ACPI) && defined(CONFIG_DYNAMIC_DEBUG) +__printf(3, 4) +void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const char *fmt, ...); +#endif + +/* + * acpi_handle_: Print message with ACPI prefix and object path + * + * These interfaces acquire the global namespace mutex to obtain an object + * path. In interrupt context, it shows the object path as . + */ +#define acpi_handle_emerg(handle, fmt, ...) \ + acpi_handle_printk(KERN_EMERG, handle, fmt, ##__VA_ARGS__) +#define acpi_handle_alert(handle, fmt, ...) \ + acpi_handle_printk(KERN_ALERT, handle, fmt, ##__VA_ARGS__) +#define acpi_handle_crit(handle, fmt, ...) \ + acpi_handle_printk(KERN_CRIT, handle, fmt, ##__VA_ARGS__) +#define acpi_handle_err(handle, fmt, ...) \ + acpi_handle_printk(KERN_ERR, handle, fmt, ##__VA_ARGS__) +#define acpi_handle_warn(handle, fmt, ...) \ + acpi_handle_printk(KERN_WARNING, handle, fmt, ##__VA_ARGS__) +#define acpi_handle_notice(handle, fmt, ...) \ + acpi_handle_printk(KERN_NOTICE, handle, fmt, ##__VA_ARGS__) +#define acpi_handle_info(handle, fmt, ...) \ + acpi_handle_printk(KERN_INFO, handle, fmt, ##__VA_ARGS__) + +#if defined(DEBUG) +#define acpi_handle_debug(handle, fmt, ...) \ + acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__) +#else +#if defined(CONFIG_DYNAMIC_DEBUG) +#define acpi_handle_debug(handle, fmt, ...) \ + _dynamic_func_call(fmt, __acpi_handle_debug, \ + handle, pr_fmt(fmt), ##__VA_ARGS__) +#else +#define acpi_handle_debug(handle, fmt, ...) \ +({ \ + if (0) \ + acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__); \ + 0; \ +}) +#endif +#endif + +#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) +bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio); +int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index); +#else +static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio) +{ + return false; +} +static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) +{ + return -ENXIO; +} +#endif + +/* Device properties */ + +#ifdef CONFIG_ACPI +int acpi_dev_get_property(const struct acpi_device *adev, const char *name, + acpi_object_type type, const union acpi_object **obj); +int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, + const char *name, size_t index, size_t num_args, + struct fwnode_reference_args *args); + +static inline int acpi_node_get_property_reference( + const struct fwnode_handle *fwnode, + const char *name, size_t index, + struct fwnode_reference_args *args) +{ + return __acpi_node_get_property_reference(fwnode, name, index, + NR_FWNODE_REFERENCE_ARGS, args); +} + +static inline bool acpi_dev_has_props(const struct acpi_device *adev) +{ + return !list_empty(&adev->data.properties); +} + +struct acpi_device_properties * +acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, + const union acpi_object *properties); + +int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, + void **valptr); +int acpi_dev_prop_read_single(struct acpi_device *adev, + const char *propname, enum dev_prop_type proptype, + void *val); +int acpi_node_prop_read(const struct fwnode_handle *fwnode, + const char *propname, enum dev_prop_type proptype, + void *val, size_t nval); +int acpi_dev_prop_read(const struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval); + +struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, + struct fwnode_handle *child); +struct fwnode_handle *acpi_node_get_parent(const struct fwnode_handle *fwnode); + +struct acpi_probe_entry; +typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, + struct acpi_probe_entry *); + +#define ACPI_TABLE_ID_LEN 5 + +/** + * struct acpi_probe_entry - boot-time probing entry + * @id: ACPI table name + * @type: Optional subtable type to match + * (if @id contains subtables) + * @subtable_valid: Optional callback to check the validity of + * the subtable + * @probe_table: Callback to the driver being probed when table + * match is successful + * @probe_subtbl: Callback to the driver being probed when table and + * subtable match (and optional callback is successful) + * @driver_data: Sideband data provided back to the driver + */ +struct acpi_probe_entry { + __u8 id[ACPI_TABLE_ID_LEN]; + __u8 type; + acpi_probe_entry_validate_subtbl subtable_valid; + union { + acpi_tbl_table_handler probe_table; + acpi_tbl_entry_handler probe_subtbl; + }; + kernel_ulong_t driver_data; +}; + +#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \ + static const struct acpi_probe_entry __acpi_probe_##name \ + __used __section(__##table##_acpi_probe_table) \ + = { \ + .id = table_id, \ + .type = subtable, \ + .subtable_valid = valid, \ + .probe_table = (acpi_tbl_table_handler)fn, \ + .driver_data = data, \ + } + +#define ACPI_PROBE_TABLE(name) __##name##_acpi_probe_table +#define ACPI_PROBE_TABLE_END(name) __##name##_acpi_probe_table_end + +int __acpi_probe_device_table(struct acpi_probe_entry *start, int nr); + +#define acpi_probe_device_table(t) \ + ({ \ + extern struct acpi_probe_entry ACPI_PROBE_TABLE(t), \ + ACPI_PROBE_TABLE_END(t); \ + __acpi_probe_device_table(&ACPI_PROBE_TABLE(t), \ + (&ACPI_PROBE_TABLE_END(t) - \ + &ACPI_PROBE_TABLE(t))); \ + }) +#else +static inline int acpi_dev_get_property(struct acpi_device *adev, + const char *name, acpi_object_type type, + const union acpi_object **obj) +{ + return -ENXIO; +} + +static inline int +__acpi_node_get_property_reference(const struct fwnode_handle *fwnode, + const char *name, size_t index, size_t num_args, + struct fwnode_reference_args *args) +{ + return -ENXIO; +} + +static inline int +acpi_node_get_property_reference(const struct fwnode_handle *fwnode, + const char *name, size_t index, + struct fwnode_reference_args *args) +{ + return -ENXIO; +} + +static inline int acpi_node_prop_get(const struct fwnode_handle *fwnode, + const char *propname, + void **valptr) +{ + return -ENXIO; +} + +static inline int acpi_dev_prop_get(const struct acpi_device *adev, + const char *propname, + void **valptr) +{ + return -ENXIO; +} + +static inline int acpi_dev_prop_read_single(const struct acpi_device *adev, + const char *propname, + enum dev_prop_type proptype, + void *val) +{ + return -ENXIO; +} + +static inline int acpi_node_prop_read(const struct fwnode_handle *fwnode, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) +{ + return -ENXIO; +} + +static inline int acpi_dev_prop_read(const struct acpi_device *adev, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) +{ + return -ENXIO; +} + +static inline struct fwnode_handle * +acpi_get_next_subnode(const struct fwnode_handle *fwnode, + struct fwnode_handle *child) +{ + return NULL; +} + +static inline struct fwnode_handle * +acpi_node_get_parent(const struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline struct fwnode_handle * +acpi_graph_get_next_endpoint(const struct fwnode_handle *fwnode, + struct fwnode_handle *prev) +{ + return ERR_PTR(-ENXIO); +} + +static inline int +acpi_graph_get_remote_endpoint(const struct fwnode_handle *fwnode, + struct fwnode_handle **remote, + struct fwnode_handle **port, + struct fwnode_handle **endpoint) +{ + return -ENXIO; +} + +#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \ + static const void * __acpi_table_##name[] \ + __attribute__((unused)) \ + = { (void *) table_id, \ + (void *) subtable, \ + (void *) valid, \ + (void *) fn, \ + (void *) data } + +#define acpi_probe_device_table(t) ({ int __r = 0; __r;}) +#endif + +#ifdef CONFIG_ACPI_TABLE_UPGRADE +void acpi_table_upgrade(void); +#else +static inline void acpi_table_upgrade(void) { } +#endif + +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_WATCHDOG) +extern bool acpi_has_watchdog(void); +#else +static inline bool acpi_has_watchdog(void) { return false; } +#endif + +#ifdef CONFIG_ACPI_SPCR_TABLE +extern bool qdf2400_e44_present; +int acpi_parse_spcr(bool enable_earlycon, bool enable_console); +#else +static inline int acpi_parse_spcr(bool enable_earlycon, bool enable_console) +{ + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_ACPI_GENERIC_GSI) +int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res); +#else +static inline +int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_ACPI_LPIT +int lpit_read_residency_count_address(u64 *address); +#else +static inline int lpit_read_residency_count_address(u64 *address) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_ACPI_PPTT +int acpi_pptt_cpu_is_thread(unsigned int cpu); +int find_acpi_cpu_topology(unsigned int cpu, int level); +int find_acpi_cpu_topology_package(unsigned int cpu); +int find_acpi_cpu_topology_hetero_id(unsigned int cpu); +int find_acpi_cpu_cache_topology(unsigned int cpu, int level); +#else +static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) +{ + return -EINVAL; +} +static inline int find_acpi_cpu_topology(unsigned int cpu, int level) +{ + return -EINVAL; +} +static inline int find_acpi_cpu_topology_package(unsigned int cpu) +{ + return -EINVAL; +} +static inline int find_acpi_cpu_topology_hetero_id(unsigned int cpu) +{ + return -EINVAL; +} +static inline int find_acpi_cpu_cache_topology(unsigned int cpu, int level) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_ACPI +extern int acpi_platform_notify(struct device *dev, enum kobject_action action); +#else +static inline int +acpi_platform_notify(struct device *dev, enum kobject_action action) +{ + return 0; +} +#endif + +#endif /*_LINUX_ACPI_H*/ diff --git a/t/tree/include/linux/apm_bios.h b/t/tree/include/linux/apm_bios.h new file mode 100644 index 00000000..7554192c --- /dev/null +++ b/t/tree/include/linux/apm_bios.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Include file for the interface to an APM BIOS + * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au) + */ +#ifndef _LINUX_APM_H +#define _LINUX_APM_H + +#include + + +#define APM_CS (GDT_ENTRY_APMBIOS_BASE * 8) +#define APM_CS_16 (APM_CS + 8) +#define APM_DS (APM_CS_16 + 8) + +/* Results of APM Installation Check */ +#define APM_16_BIT_SUPPORT 0x0001 +#define APM_32_BIT_SUPPORT 0x0002 +#define APM_IDLE_SLOWS_CLOCK 0x0004 +#define APM_BIOS_DISABLED 0x0008 +#define APM_BIOS_DISENGAGED 0x0010 + +/* + * Data for APM that is persistent across module unload/load + */ +struct apm_info { + struct apm_bios_info bios; + unsigned short connection_version; + int get_power_status_broken; + int get_power_status_swabinminutes; + int allow_ints; + int forbid_idle; + int realmode_power_off; + int disabled; +}; + +/* + * The APM function codes + */ +#define APM_FUNC_INST_CHECK 0x5300 +#define APM_FUNC_REAL_CONN 0x5301 +#define APM_FUNC_16BIT_CONN 0x5302 +#define APM_FUNC_32BIT_CONN 0x5303 +#define APM_FUNC_DISCONN 0x5304 +#define APM_FUNC_IDLE 0x5305 +#define APM_FUNC_BUSY 0x5306 +#define APM_FUNC_SET_STATE 0x5307 +#define APM_FUNC_ENABLE_PM 0x5308 +#define APM_FUNC_RESTORE_BIOS 0x5309 +#define APM_FUNC_GET_STATUS 0x530a +#define APM_FUNC_GET_EVENT 0x530b +#define APM_FUNC_GET_STATE 0x530c +#define APM_FUNC_ENABLE_DEV_PM 0x530d +#define APM_FUNC_VERSION 0x530e +#define APM_FUNC_ENGAGE_PM 0x530f +#define APM_FUNC_GET_CAP 0x5310 +#define APM_FUNC_RESUME_TIMER 0x5311 +#define APM_FUNC_RESUME_ON_RING 0x5312 +#define APM_FUNC_TIMER 0x5313 + +/* + * Function code for APM_FUNC_RESUME_TIMER + */ +#define APM_FUNC_DISABLE_TIMER 0 +#define APM_FUNC_GET_TIMER 1 +#define APM_FUNC_SET_TIMER 2 + +/* + * Function code for APM_FUNC_RESUME_ON_RING + */ +#define APM_FUNC_DISABLE_RING 0 +#define APM_FUNC_ENABLE_RING 1 +#define APM_FUNC_GET_RING 2 + +/* + * Function code for APM_FUNC_TIMER_STATUS + */ +#define APM_FUNC_TIMER_DISABLE 0 +#define APM_FUNC_TIMER_ENABLE 1 +#define APM_FUNC_TIMER_GET 2 + +/* + * in arch/i386/kernel/setup.c + */ +extern struct apm_info apm_info; + +/* + * This is the "All Devices" ID communicated to the BIOS + */ +#define APM_DEVICE_BALL ((apm_info.connection_version > 0x0100) ? \ + APM_DEVICE_ALL : APM_DEVICE_OLD_ALL) +#endif /* LINUX_APM_H */ diff --git a/t/tree/include/linux/assoc_array.h b/t/tree/include/linux/assoc_array.h new file mode 100644 index 00000000..8b3f230c --- /dev/null +++ b/t/tree/include/linux/assoc_array.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Generic associative array implementation. + * + * See Documentation/core-api/assoc_array.rst for information. + * + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _LINUX_ASSOC_ARRAY_H +#define _LINUX_ASSOC_ARRAY_H + +#ifdef CONFIG_ASSOCIATIVE_ARRAY + +#include + +#define ASSOC_ARRAY_KEY_CHUNK_SIZE BITS_PER_LONG /* Key data retrieved in chunks of this size */ + +/* + * Generic associative array. + */ +struct assoc_array { + struct assoc_array_ptr *root; /* The node at the root of the tree */ + unsigned long nr_leaves_on_tree; +}; + +/* + * Operations on objects and index keys for use by array manipulation routines. + */ +struct assoc_array_ops { + /* Method to get a chunk of an index key from caller-supplied data */ + unsigned long (*get_key_chunk)(const void *index_key, int level); + + /* Method to get a piece of an object's index key */ + unsigned long (*get_object_key_chunk)(const void *object, int level); + + /* Is this the object we're looking for? */ + bool (*compare_object)(const void *object, const void *index_key); + + /* How different is an object from an index key, to a bit position in + * their keys? (or -1 if they're the same) + */ + int (*diff_objects)(const void *object, const void *index_key); + + /* Method to free an object. */ + void (*free_object)(void *object); +}; + +/* + * Access and manipulation functions. + */ +struct assoc_array_edit; + +static inline void assoc_array_init(struct assoc_array *array) +{ + array->root = NULL; + array->nr_leaves_on_tree = 0; +} + +extern int assoc_array_iterate(const struct assoc_array *array, + int (*iterator)(const void *object, + void *iterator_data), + void *iterator_data); +extern void *assoc_array_find(const struct assoc_array *array, + const struct assoc_array_ops *ops, + const void *index_key); +extern void assoc_array_destroy(struct assoc_array *array, + const struct assoc_array_ops *ops); +extern struct assoc_array_edit *assoc_array_insert(struct assoc_array *array, + const struct assoc_array_ops *ops, + const void *index_key, + void *object); +extern void assoc_array_insert_set_object(struct assoc_array_edit *edit, + void *object); +extern struct assoc_array_edit *assoc_array_delete(struct assoc_array *array, + const struct assoc_array_ops *ops, + const void *index_key); +extern struct assoc_array_edit *assoc_array_clear(struct assoc_array *array, + const struct assoc_array_ops *ops); +extern void assoc_array_apply_edit(struct assoc_array_edit *edit); +extern void assoc_array_cancel_edit(struct assoc_array_edit *edit); +extern int assoc_array_gc(struct assoc_array *array, + const struct assoc_array_ops *ops, + bool (*iterator)(void *object, void *iterator_data), + void *iterator_data); + +#endif /* CONFIG_ASSOCIATIVE_ARRAY */ +#endif /* _LINUX_ASSOC_ARRAY_H */ diff --git a/t/tree/include/linux/cred.h b/t/tree/include/linux/cred.h new file mode 100644 index 00000000..18639c06 --- /dev/null +++ b/t/tree/include/linux/cred.h @@ -0,0 +1,425 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Credentials management - see Documentation/security/credentials.rst + * + * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _LINUX_CRED_H +#define _LINUX_CRED_H + +#include +#include +#include +#include +#include +#include +#include + +struct cred; +struct inode; + +/* + * COW Supplementary groups list + */ +struct group_info { + atomic_t usage; + int ngroups; + kgid_t gid[0]; +} __randomize_layout; + +/** + * get_group_info - Get a reference to a group info structure + * @group_info: The group info to reference + * + * This gets a reference to a set of supplementary groups. + * + * If the caller is accessing a task's credentials, they must hold the RCU read + * lock when reading. + */ +static inline struct group_info *get_group_info(struct group_info *gi) +{ + atomic_inc(&gi->usage); + return gi; +} + +/** + * put_group_info - Release a reference to a group info structure + * @group_info: The group info to release + */ +#define put_group_info(group_info) \ +do { \ + if (atomic_dec_and_test(&(group_info)->usage)) \ + groups_free(group_info); \ +} while (0) + +extern struct group_info init_groups; +#ifdef CONFIG_MULTIUSER +extern struct group_info *groups_alloc(int); +extern void groups_free(struct group_info *); + +extern int in_group_p(kgid_t); +extern int in_egroup_p(kgid_t); +extern int groups_search(const struct group_info *, kgid_t); + +extern int set_current_groups(struct group_info *); +extern void set_groups(struct cred *, struct group_info *); +extern bool may_setgroups(void); +extern void groups_sort(struct group_info *); +#else +static inline void groups_free(struct group_info *group_info) +{ +} + +static inline int in_group_p(kgid_t grp) +{ + return 1; +} +static inline int in_egroup_p(kgid_t grp) +{ + return 1; +} +static inline int groups_search(const struct group_info *group_info, kgid_t grp) +{ + return 1; +} +#endif + +/* + * The security context of a task + * + * The parts of the context break down into two categories: + * + * (1) The objective context of a task. These parts are used when some other + * task is attempting to affect this one. + * + * (2) The subjective context. These details are used when the task is acting + * upon another object, be that a file, a task, a key or whatever. + * + * Note that some members of this structure belong to both categories - the + * LSM security pointer for instance. + * + * A task has two security pointers. task->real_cred points to the objective + * context that defines that task's actual details. The objective part of this + * context is used whenever that task is acted upon. + * + * task->cred points to the subjective context that defines the details of how + * that task is going to act upon another object. This may be overridden + * temporarily to point to another security context, but normally points to the + * same context as task->real_cred. + */ +struct cred { + atomic_t usage; +#ifdef CONFIG_DEBUG_CREDENTIALS + atomic_t subscribers; /* number of processes subscribed */ + void *put_addr; + unsigned magic; +#define CRED_MAGIC 0x43736564 +#define CRED_MAGIC_DEAD 0x44656144 +#endif + kuid_t uid; /* real UID of the task */ + kgid_t gid; /* real GID of the task */ + kuid_t suid; /* saved UID of the task */ + kgid_t sgid; /* saved GID of the task */ + kuid_t euid; /* effective UID of the task */ + kgid_t egid; /* effective GID of the task */ + kuid_t fsuid; /* UID for VFS ops */ + kgid_t fsgid; /* GID for VFS ops */ + unsigned securebits; /* SUID-less security management */ + kernel_cap_t cap_inheritable; /* caps our children can inherit */ + kernel_cap_t cap_permitted; /* caps we're permitted */ + kernel_cap_t cap_effective; /* caps we can actually use */ + kernel_cap_t cap_bset; /* capability bounding set */ + kernel_cap_t cap_ambient; /* Ambient capability set */ +#ifdef CONFIG_KEYS + unsigned char jit_keyring; /* default keyring to attach requested + * keys to */ + struct key *session_keyring; /* keyring inherited over fork */ + struct key *process_keyring; /* keyring private to this process */ + struct key *thread_keyring; /* keyring private to this thread */ + struct key *request_key_auth; /* assumed request_key authority */ +#endif +#ifdef CONFIG_SECURITY + void *security; /* subjective LSM security */ +#endif + struct user_struct *user; /* real user ID subscription */ + struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ + struct group_info *group_info; /* supplementary groups for euid/fsgid */ + /* RCU deletion */ + union { + int non_rcu; /* Can we skip RCU deletion? */ + struct rcu_head rcu; /* RCU deletion hook */ + }; +} __randomize_layout; + +extern void __put_cred(struct cred *); +extern void exit_creds(struct task_struct *); +extern int copy_creds(struct task_struct *, unsigned long); +extern const struct cred *get_task_cred(struct task_struct *); +extern struct cred *cred_alloc_blank(void); +extern struct cred *prepare_creds(void); +extern struct cred *prepare_exec_creds(void); +extern int commit_creds(struct cred *); +extern void abort_creds(struct cred *); +extern const struct cred *override_creds(const struct cred *); +extern void revert_creds(const struct cred *); +extern struct cred *prepare_kernel_cred(struct task_struct *); +extern int change_create_files_as(struct cred *, struct inode *); +extern int set_security_override(struct cred *, u32); +extern int set_security_override_from_ctx(struct cred *, const char *); +extern int set_create_files_as(struct cred *, struct inode *); +extern int cred_fscmp(const struct cred *, const struct cred *); +extern void __init cred_init(void); + +/* + * check for validity of credentials + */ +#ifdef CONFIG_DEBUG_CREDENTIALS +extern void __invalid_creds(const struct cred *, const char *, unsigned); +extern void __validate_process_creds(struct task_struct *, + const char *, unsigned); + +extern bool creds_are_invalid(const struct cred *cred); + +static inline void __validate_creds(const struct cred *cred, + const char *file, unsigned line) +{ + if (unlikely(creds_are_invalid(cred))) + __invalid_creds(cred, file, line); +} + +#define validate_creds(cred) \ +do { \ + __validate_creds((cred), __FILE__, __LINE__); \ +} while(0) + +#define validate_process_creds() \ +do { \ + __validate_process_creds(current, __FILE__, __LINE__); \ +} while(0) + +extern void validate_creds_for_do_exit(struct task_struct *); +#else +static inline void validate_creds(const struct cred *cred) +{ +} +static inline void validate_creds_for_do_exit(struct task_struct *tsk) +{ +} +static inline void validate_process_creds(void) +{ +} +#endif + +static inline bool cap_ambient_invariant_ok(const struct cred *cred) +{ + return cap_issubset(cred->cap_ambient, + cap_intersect(cred->cap_permitted, + cred->cap_inheritable)); +} + +/** + * get_new_cred - Get a reference on a new set of credentials + * @cred: The new credentials to reference + * + * Get a reference on the specified set of new credentials. The caller must + * release the reference. + */ +static inline struct cred *get_new_cred(struct cred *cred) +{ + atomic_inc(&cred->usage); + return cred; +} + +/** + * get_cred - Get a reference on a set of credentials + * @cred: The credentials to reference + * + * Get a reference on the specified set of credentials. The caller must + * release the reference. If %NULL is passed, it is returned with no action. + * + * This is used to deal with a committed set of credentials. Although the + * pointer is const, this will temporarily discard the const and increment the + * usage count. The purpose of this is to attempt to catch at compile time the + * accidental alteration of a set of credentials that should be considered + * immutable. + */ +static inline const struct cred *get_cred(const struct cred *cred) +{ + struct cred *nonconst_cred = (struct cred *) cred; + if (!cred) + return cred; + validate_creds(cred); + nonconst_cred->non_rcu = 0; + return get_new_cred(nonconst_cred); +} + +static inline const struct cred *get_cred_rcu(const struct cred *cred) +{ + struct cred *nonconst_cred = (struct cred *) cred; + if (!cred) + return NULL; + if (!atomic_inc_not_zero(&nonconst_cred->usage)) + return NULL; + validate_creds(cred); + nonconst_cred->non_rcu = 0; + return cred; +} + +/** + * put_cred - Release a reference to a set of credentials + * @cred: The credentials to release + * + * Release a reference to a set of credentials, deleting them when the last ref + * is released. If %NULL is passed, nothing is done. + * + * This takes a const pointer to a set of credentials because the credentials + * on task_struct are attached by const pointers to prevent accidental + * alteration of otherwise immutable credential sets. + */ +static inline void put_cred(const struct cred *_cred) +{ + struct cred *cred = (struct cred *) _cred; + + if (cred) { + validate_creds(cred); + if (atomic_dec_and_test(&(cred)->usage)) + __put_cred(cred); + } +} + +/** + * current_cred - Access the current task's subjective credentials + * + * Access the subjective credentials of the current task. RCU-safe, + * since nobody else can modify it. + */ +#define current_cred() \ + rcu_dereference_protected(current->cred, 1) + +/** + * current_real_cred - Access the current task's objective credentials + * + * Access the objective credentials of the current task. RCU-safe, + * since nobody else can modify it. + */ +#define current_real_cred() \ + rcu_dereference_protected(current->real_cred, 1) + +/** + * __task_cred - Access a task's objective credentials + * @task: The task to query + * + * Access the objective credentials of a task. The caller must hold the RCU + * readlock. + * + * The result of this function should not be passed directly to get_cred(); + * rather get_task_cred() should be used instead. + */ +#define __task_cred(task) \ + rcu_dereference((task)->real_cred) + +/** + * get_current_cred - Get the current task's subjective credentials + * + * Get the subjective credentials of the current task, pinning them so that + * they can't go away. Accessing the current task's credentials directly is + * not permitted. + */ +#define get_current_cred() \ + (get_cred(current_cred())) + +/** + * get_current_user - Get the current task's user_struct + * + * Get the user record of the current task, pinning it so that it can't go + * away. + */ +#define get_current_user() \ +({ \ + struct user_struct *__u; \ + const struct cred *__cred; \ + __cred = current_cred(); \ + __u = get_uid(__cred->user); \ + __u; \ +}) + +/** + * get_current_groups - Get the current task's supplementary group list + * + * Get the supplementary group list of the current task, pinning it so that it + * can't go away. + */ +#define get_current_groups() \ +({ \ + struct group_info *__groups; \ + const struct cred *__cred; \ + __cred = current_cred(); \ + __groups = get_group_info(__cred->group_info); \ + __groups; \ +}) + +#define task_cred_xxx(task, xxx) \ +({ \ + __typeof__(((struct cred *)NULL)->xxx) ___val; \ + rcu_read_lock(); \ + ___val = __task_cred((task))->xxx; \ + rcu_read_unlock(); \ + ___val; \ +}) + +#define task_uid(task) (task_cred_xxx((task), uid)) +#define task_euid(task) (task_cred_xxx((task), euid)) + +#define current_cred_xxx(xxx) \ +({ \ + current_cred()->xxx; \ +}) + +#define current_uid() (current_cred_xxx(uid)) +#define current_gid() (current_cred_xxx(gid)) +#define current_euid() (current_cred_xxx(euid)) +#define current_egid() (current_cred_xxx(egid)) +#define current_suid() (current_cred_xxx(suid)) +#define current_sgid() (current_cred_xxx(sgid)) +#define current_fsuid() (current_cred_xxx(fsuid)) +#define current_fsgid() (current_cred_xxx(fsgid)) +#define current_cap() (current_cred_xxx(cap_effective)) +#define current_user() (current_cred_xxx(user)) + +extern struct user_namespace init_user_ns; +#ifdef CONFIG_USER_NS +#define current_user_ns() (current_cred_xxx(user_ns)) +#else +static inline struct user_namespace *current_user_ns(void) +{ + return &init_user_ns; +} +#endif + + +#define current_uid_gid(_uid, _gid) \ +do { \ + const struct cred *__cred; \ + __cred = current_cred(); \ + *(_uid) = __cred->uid; \ + *(_gid) = __cred->gid; \ +} while(0) + +#define current_euid_egid(_euid, _egid) \ +do { \ + const struct cred *__cred; \ + __cred = current_cred(); \ + *(_euid) = __cred->euid; \ + *(_egid) = __cred->egid; \ +} while(0) + +#define current_fsuid_fsgid(_fsuid, _fsgid) \ +do { \ + const struct cred *__cred; \ + __cred = current_cred(); \ + *(_fsuid) = __cred->fsuid; \ + *(_fsgid) = __cred->fsgid; \ +} while(0) + +#endif /* _LINUX_CRED_H */ diff --git a/t/tree/include/linux/i2c-smbus.h b/t/tree/include/linux/i2c-smbus.h new file mode 100644 index 00000000..585ad6fc --- /dev/null +++ b/t/tree/include/linux/i2c-smbus.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * i2c-smbus.h - SMBus extensions to the I2C protocol + * + * Copyright (C) 2010 Jean Delvare + */ + +#ifndef _LINUX_I2C_SMBUS_H +#define _LINUX_I2C_SMBUS_H + +#include +#include +#include + + +/** + * i2c_smbus_alert_setup - platform data for the smbus_alert i2c client + * @alert_edge_triggered: whether the alert interrupt is edge (1) or level (0) + * triggered + * @irq: IRQ number, if the smbus_alert driver should take care of interrupt + * handling + * + * If irq is not specified, the smbus_alert driver doesn't take care of + * interrupt handling. In that case it is up to the I2C bus driver to either + * handle the interrupts or to poll for alerts. + * + * If irq is specified then it it crucial that alert_edge_triggered is + * properly set. + */ +struct i2c_smbus_alert_setup { + int irq; +}; + +struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, + struct i2c_smbus_alert_setup *setup); +int i2c_handle_smbus_alert(struct i2c_client *ara); + +#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) +int of_i2c_setup_smbus_alert(struct i2c_adapter *adap); +#else +static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap) +{ + return 0; +} +#endif + +#endif /* _LINUX_I2C_SMBUS_H */ diff --git a/t/tree/include/linux/i2c.h b/t/tree/include/linux/i2c.h new file mode 100644 index 00000000..1361637c --- /dev/null +++ b/t/tree/include/linux/i2c.h @@ -0,0 +1,1008 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * i2c.h - definitions for the Linux i2c bus interface + * Copyright (C) 1995-2000 Simon G. Vogl + * Copyright (C) 2013-2019 Wolfram Sang + * + * With some changes from Kyösti Mälkki and + * Frodo Looijaard + */ +#ifndef _LINUX_I2C_H +#define _LINUX_I2C_H + +#include /* for acpi_handle */ +#include +#include /* for struct device */ +#include /* for completion */ +#include +#include +#include /* for Host Notify IRQ */ +#include /* for struct device_node */ +#include /* for swab16 */ +#include + +extern struct bus_type i2c_bus_type; +extern struct device_type i2c_adapter_type; +extern struct device_type i2c_client_type; + +/* --- General options ------------------------------------------------ */ + +struct i2c_msg; +struct i2c_algorithm; +struct i2c_adapter; +struct i2c_client; +struct i2c_driver; +struct i2c_device_identity; +union i2c_smbus_data; +struct i2c_board_info; +enum i2c_slave_event; +typedef int (*i2c_slave_cb_t)(struct i2c_client *client, + enum i2c_slave_event event, u8 *val); + +struct module; +struct property_entry; + +#if IS_ENABLED(CONFIG_I2C) +/* + * The master routines are the ones normally used to transmit data to devices + * on a bus (or read from them). Apart from two basic transfer functions to + * transmit one message at a time, a more complex version can be used to + * transmit an arbitrary number of messages without interruption. + * @count must be be less than 64k since msg.len is u16. + */ +extern int i2c_transfer_buffer_flags(const struct i2c_client *client, + char *buf, int count, u16 flags); + +/** + * i2c_master_recv - issue a single I2C message in master receive mode + * @client: Handle to slave device + * @buf: Where to store data read from slave + * @count: How many bytes to read, must be less than 64k since msg.len is u16 + * + * Returns negative errno, or else the number of bytes read. + */ +static inline int i2c_master_recv(const struct i2c_client *client, + char *buf, int count) +{ + return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD); +}; + +/** + * i2c_master_recv_dmasafe - issue a single I2C message in master receive mode + * using a DMA safe buffer + * @client: Handle to slave device + * @buf: Where to store data read from slave, must be safe to use with DMA + * @count: How many bytes to read, must be less than 64k since msg.len is u16 + * + * Returns negative errno, or else the number of bytes read. + */ +static inline int i2c_master_recv_dmasafe(const struct i2c_client *client, + char *buf, int count) +{ + return i2c_transfer_buffer_flags(client, buf, count, + I2C_M_RD | I2C_M_DMA_SAFE); +}; + +/** + * i2c_master_send - issue a single I2C message in master transmit mode + * @client: Handle to slave device + * @buf: Data that will be written to the slave + * @count: How many bytes to write, must be less than 64k since msg.len is u16 + * + * Returns negative errno, or else the number of bytes written. + */ +static inline int i2c_master_send(const struct i2c_client *client, + const char *buf, int count) +{ + return i2c_transfer_buffer_flags(client, (char *)buf, count, 0); +}; + +/** + * i2c_master_send_dmasafe - issue a single I2C message in master transmit mode + * using a DMA safe buffer + * @client: Handle to slave device + * @buf: Data that will be written to the slave, must be safe to use with DMA + * @count: How many bytes to write, must be less than 64k since msg.len is u16 + * + * Returns negative errno, or else the number of bytes written. + */ +static inline int i2c_master_send_dmasafe(const struct i2c_client *client, + const char *buf, int count) +{ + return i2c_transfer_buffer_flags(client, (char *)buf, count, + I2C_M_DMA_SAFE); +}; + +/* Transfer num messages. + */ +extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); +/* Unlocked flavor */ +extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); + +/* This is the very generalized SMBus access routine. You probably do not + want to use this, though; one of the functions below may be much easier, + and probably just as fast. + Note that we use i2c_adapter here, because you do not need a specific + smbus adapter to call this function. */ +s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int protocol, union i2c_smbus_data *data); + +/* Unlocked flavor */ +s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int protocol, union i2c_smbus_data *data); + +/* Now follow the 'nice' access routines. These also document the calling + conventions of i2c_smbus_xfer. */ + +extern s32 i2c_smbus_read_byte(const struct i2c_client *client); +extern s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value); +extern s32 i2c_smbus_read_byte_data(const struct i2c_client *client, + u8 command); +extern s32 i2c_smbus_write_byte_data(const struct i2c_client *client, + u8 command, u8 value); +extern s32 i2c_smbus_read_word_data(const struct i2c_client *client, + u8 command); +extern s32 i2c_smbus_write_word_data(const struct i2c_client *client, + u8 command, u16 value); + +static inline s32 +i2c_smbus_read_word_swapped(const struct i2c_client *client, u8 command) +{ + s32 value = i2c_smbus_read_word_data(client, command); + + return (value < 0) ? value : swab16(value); +} + +static inline s32 +i2c_smbus_write_word_swapped(const struct i2c_client *client, + u8 command, u16 value) +{ + return i2c_smbus_write_word_data(client, command, swab16(value)); +} + +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(const struct i2c_client *client, + u8 command, u8 *values); +extern s32 i2c_smbus_write_block_data(const struct i2c_client *client, + u8 command, u8 length, const u8 *values); +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, u8 *values); +extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, + u8 command, u8 length, + const u8 *values); +extern s32 +i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, + u8 command, u8 length, u8 *values); +int i2c_get_device_id(const struct i2c_client *client, + struct i2c_device_identity *id); +#endif /* I2C */ + +/** + * struct i2c_device_identity - i2c client device identification + * @manufacturer_id: 0 - 4095, database maintained by NXP + * @part_id: 0 - 511, according to manufacturer + * @die_revision: 0 - 7, according to manufacturer + */ +struct i2c_device_identity { + u16 manufacturer_id; +#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS 0 +#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_1 1 +#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_2 2 +#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_3 3 +#define I2C_DEVICE_ID_RAMTRON_INTERNATIONAL 4 +#define I2C_DEVICE_ID_ANALOG_DEVICES 5 +#define I2C_DEVICE_ID_STMICROELECTRONICS 6 +#define I2C_DEVICE_ID_ON_SEMICONDUCTOR 7 +#define I2C_DEVICE_ID_SPRINTEK_CORPORATION 8 +#define I2C_DEVICE_ID_ESPROS_PHOTONICS_AG 9 +#define I2C_DEVICE_ID_FUJITSU_SEMICONDUCTOR 10 +#define I2C_DEVICE_ID_FLIR 11 +#define I2C_DEVICE_ID_O2MICRO 12 +#define I2C_DEVICE_ID_ATMEL 13 +#define I2C_DEVICE_ID_NONE 0xffff + u16 part_id; + u8 die_revision; +}; + +enum i2c_alert_protocol { + I2C_PROTOCOL_SMBUS_ALERT, + I2C_PROTOCOL_SMBUS_HOST_NOTIFY, +}; + +/** + * struct i2c_driver - represent an I2C device driver + * @class: What kind of i2c device we instantiate (for detect) + * @probe: Callback for device binding - soon to be deprecated + * @probe_new: New callback for device binding + * @remove: Callback for device unbinding + * @shutdown: Callback for device shutdown + * @alert: Alert callback, for example for the SMBus alert protocol + * @command: Callback for bus-wide signaling (optional) + * @driver: Device driver model driver + * @id_table: List of I2C devices supported by this driver + * @detect: Callback for device detection + * @address_list: The I2C addresses to probe (for detect) + * @clients: List of detected clients we created (for i2c-core use only) + * @disable_i2c_core_irq_mapping: Tell the i2c-core to not do irq-mapping + * + * The driver.owner field should be set to the module owner of this driver. + * The driver.name field should be set to the name of this driver. + * + * For automatic device detection, both @detect and @address_list must + * be defined. @class should also be set, otherwise only devices forced + * with module parameters will be created. The detect function must + * fill at least the name field of the i2c_board_info structure it is + * handed upon successful detection, and possibly also the flags field. + * + * If @detect is missing, the driver will still work fine for enumerated + * devices. Detected devices simply won't be supported. This is expected + * for the many I2C/SMBus devices which can't be detected reliably, and + * the ones which can always be enumerated in practice. + * + * The i2c_client structure which is handed to the @detect callback is + * not a real i2c_client. It is initialized just enough so that you can + * call i2c_smbus_read_byte_data and friends on it. Don't do anything + * else with it. In particular, calling dev_dbg and friends on it is + * not allowed. + */ +struct i2c_driver { + unsigned int class; + + /* Standard driver model interfaces */ + int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); + int (*remove)(struct i2c_client *client); + + /* New driver model interface to aid the seamless removal of the + * current probe()'s, more commonly unused than used second parameter. + */ + int (*probe_new)(struct i2c_client *client); + + /* driver model interfaces that don't relate to enumeration */ + void (*shutdown)(struct i2c_client *client); + + /* Alert callback, for example for the SMBus alert protocol. + * The format and meaning of the data value depends on the protocol. + * For the SMBus alert protocol, there is a single bit of data passed + * as the alert response's low bit ("event flag"). + * For the SMBus Host Notify protocol, the data corresponds to the + * 16-bit payload data reported by the slave device acting as master. + */ + void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, + unsigned int data); + + /* a ioctl like command that can be used to perform specific functions + * with the device. + */ + int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); + + struct device_driver driver; + const struct i2c_device_id *id_table; + + /* Device detection callback for automatic device creation */ + int (*detect)(struct i2c_client *client, struct i2c_board_info *info); + const unsigned short *address_list; + struct list_head clients; + + bool disable_i2c_core_irq_mapping; +}; +#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver) + +/** + * struct i2c_client - represent an I2C slave device + * @flags: see I2C_CLIENT_* for possible flags + * @addr: Address used on the I2C bus connected to the parent adapter. + * @name: Indicates the type of the device, usually a chip name that's + * generic enough to hide second-sourcing and compatible revisions. + * @adapter: manages the bus segment hosting this I2C device + * @dev: Driver model device node for the slave. + * @irq: indicates the IRQ generated by this device (if any) + * @detected: member of an i2c_driver.clients list or i2c-core's + * userspace_devices list + * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter + * calls it to pass on slave events to the slave driver. + * + * An i2c_client identifies a single device (i.e. chip) connected to an + * i2c bus. The behaviour exposed to Linux is defined by the driver + * managing the device. + */ +struct i2c_client { + unsigned short flags; /* div., see below */ +#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ +#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ + /* Must equal I2C_M_TEN below */ +#define I2C_CLIENT_SLAVE 0x20 /* we are the slave */ +#define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */ +#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */ +#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ + /* Must match I2C_M_STOP|IGNORE_NAK */ + + unsigned short addr; /* chip address - NOTE: 7bit */ + /* addresses are stored in the */ + /* _LOWER_ 7 bits */ + char name[I2C_NAME_SIZE]; + struct i2c_adapter *adapter; /* the adapter we sit on */ + struct device dev; /* the device structure */ + int init_irq; /* irq set at initialization */ + int irq; /* irq issued by device */ + struct list_head detected; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + i2c_slave_cb_t slave_cb; /* callback for slave mode */ +#endif +}; +#define to_i2c_client(d) container_of(d, struct i2c_client, dev) + +extern struct i2c_client *i2c_verify_client(struct device *dev); +extern struct i2c_adapter *i2c_verify_adapter(struct device *dev); +extern const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, + const struct i2c_client *client); + +static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj) +{ + struct device * const dev = container_of(kobj, struct device, kobj); + return to_i2c_client(dev); +} + +static inline void *i2c_get_clientdata(const struct i2c_client *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +/* I2C slave support */ + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +enum i2c_slave_event { + I2C_SLAVE_READ_REQUESTED, + I2C_SLAVE_WRITE_REQUESTED, + I2C_SLAVE_READ_PROCESSED, + I2C_SLAVE_WRITE_RECEIVED, + I2C_SLAVE_STOP, +}; + +extern int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb); +extern int i2c_slave_unregister(struct i2c_client *client); +extern bool i2c_detect_slave_mode(struct device *dev); + +static inline int i2c_slave_event(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + return client->slave_cb(client, event, val); +} +#else +static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } +#endif + +/** + * struct i2c_board_info - template for device creation + * @type: chip type, to initialize i2c_client.name + * @flags: to initialize i2c_client.flags + * @addr: stored in i2c_client.addr + * @dev_name: Overrides the default - dev_name if set + * @platform_data: stored in i2c_client.dev.platform_data + * @of_node: pointer to OpenFirmware device node + * @fwnode: device node supplied by the platform firmware + * @properties: additional device properties for the device + * @resources: resources associated with the device + * @num_resources: number of resources in the @resources array + * @irq: stored in i2c_client.irq + * + * I2C doesn't actually support hardware probing, although controllers and + * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's + * a device at a given address. Drivers commonly need more information than + * that, such as chip type, configuration, associated IRQ, and so on. + * + * i2c_board_info is used to build tables of information listing I2C devices + * that are present. This information is used to grow the driver model tree. + * For mainboards this is done statically using i2c_register_board_info(); + * bus numbers identify adapters that aren't yet available. For add-on boards, + * i2c_new_device() does this dynamically with the adapter already known. + */ +struct i2c_board_info { + char type[I2C_NAME_SIZE]; + unsigned short flags; + unsigned short addr; + const char *dev_name; + void *platform_data; + struct device_node *of_node; + struct fwnode_handle *fwnode; + const struct property_entry *properties; + const struct resource *resources; + unsigned int num_resources; + int irq; +}; + +/** + * I2C_BOARD_INFO - macro used to list an i2c device and its address + * @dev_type: identifies the device type + * @dev_addr: the device's address on the bus. + * + * This macro initializes essential fields of a struct i2c_board_info, + * declaring what has been provided on a particular board. Optional + * fields (such as associated irq, or device-specific platform_data) + * are provided using conventional syntax. + */ +#define I2C_BOARD_INFO(dev_type, dev_addr) \ + .type = dev_type, .addr = (dev_addr) + + +#if IS_ENABLED(CONFIG_I2C) +/* Add-on boards should register/unregister their devices; e.g. a board + * with integrated I2C, a config eeprom, sensors, and a codec that's + * used in conjunction with the primary hardware. + */ +extern struct i2c_client * +i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info); + +extern struct i2c_client * +i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info); + +/* If you don't know the exact address of an I2C device, use this variant + * instead, which can probe for device presence in a list of possible + * addresses. The "probe" callback function is optional. If it is provided, + * it must return 1 on successful probe, 0 otherwise. If it is not provided, + * a default probing method is used. + */ +extern struct i2c_client * +i2c_new_probed_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + unsigned short const *addr_list, + int (*probe)(struct i2c_adapter *adap, unsigned short addr)); + +/* Common custom probe functions */ +extern int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr); + +/* For devices that use several addresses, use i2c_new_dummy() to make + * client handles for the extra addresses. + */ +extern struct i2c_client * +i2c_new_dummy(struct i2c_adapter *adap, u16 address); + +extern struct i2c_client * +i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address); + +extern struct i2c_client * +devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adap, u16 address); + +extern struct i2c_client * +i2c_new_ancillary_device(struct i2c_client *client, + const char *name, + u16 default_addr); + +extern void i2c_unregister_device(struct i2c_client *client); +#endif /* I2C */ + +/* Mainboard arch_initcall() code should register all its I2C devices. + * This is done at arch_initcall time, before declaring any i2c adapters. + * Modules for add-on boards must use other calls. + */ +#ifdef CONFIG_I2C_BOARDINFO +extern int +i2c_register_board_info(int busnum, struct i2c_board_info const *info, + unsigned n); +#else +static inline int +i2c_register_board_info(int busnum, struct i2c_board_info const *info, + unsigned n) +{ + return 0; +} +#endif /* I2C_BOARDINFO */ + +/** + * struct i2c_algorithm - represent I2C transfer method + * @master_xfer: Issue a set of i2c transactions to the given I2C adapter + * defined by the msgs array, with num messages available to transfer via + * the adapter specified by adap. + * @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context + * so e.g. PMICs can be accessed very late before shutdown. Optional. + * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this + * is not present, then the bus layer will try and convert the SMBus calls + * into I2C transfers instead. + * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context + * so e.g. PMICs can be accessed very late before shutdown. Optional. + * @functionality: Return the flags that this algorithm/adapter pair supports + * from the I2C_FUNC_* flags. + * @reg_slave: Register given client to I2C slave mode of this adapter + * @unreg_slave: Unregister given client from I2C slave mode of this adapter + * + * The following structs are for those who like to implement new bus drivers: + * i2c_algorithm is the interface to a class of hardware solutions which can + * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 + * to name two of the most common. + * + * The return codes from the @master_xfer{_atomic} fields should indicate the + * type of error code that occurred during the transfer, as documented in the + * Kernel Documentation file Documentation/i2c/fault-codes.rst. + */ +struct i2c_algorithm { + /* + * If an adapter algorithm can't do I2C-level access, set master_xfer + * to NULL. If an adapter algorithm can do SMBus access, set + * smbus_xfer. If set to NULL, the SMBus protocol is simulated + * using common I2C messages. + * + * master_xfer should return the number of messages successfully + * processed, or a negative value on error + */ + int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); + int (*master_xfer_atomic)(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num); + int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); + int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); + + /* To determine what the adapter supports */ + u32 (*functionality)(struct i2c_adapter *adap); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + int (*reg_slave)(struct i2c_client *client); + int (*unreg_slave)(struct i2c_client *client); +#endif +}; + +/** + * struct i2c_lock_operations - represent I2C locking operations + * @lock_bus: Get exclusive access to an I2C bus segment + * @trylock_bus: Try to get exclusive access to an I2C bus segment + * @unlock_bus: Release exclusive access to an I2C bus segment + * + * The main operations are wrapped by i2c_lock_bus and i2c_unlock_bus. + */ +struct i2c_lock_operations { + void (*lock_bus)(struct i2c_adapter *adapter, unsigned int flags); + int (*trylock_bus)(struct i2c_adapter *adapter, unsigned int flags); + void (*unlock_bus)(struct i2c_adapter *adapter, unsigned int flags); +}; + +/** + * struct i2c_timings - I2C timing information + * @bus_freq_hz: the bus frequency in Hz + * @scl_rise_ns: time SCL signal takes to rise in ns; t(r) in the I2C specification + * @scl_fall_ns: time SCL signal takes to fall in ns; t(f) in the I2C specification + * @scl_int_delay_ns: time IP core additionally needs to setup SCL in ns + * @sda_fall_ns: time SDA signal takes to fall in ns; t(f) in the I2C specification + * @sda_hold_ns: time IP core additionally needs to hold SDA in ns + */ +struct i2c_timings { + u32 bus_freq_hz; + u32 scl_rise_ns; + u32 scl_fall_ns; + u32 scl_int_delay_ns; + u32 sda_fall_ns; + u32 sda_hold_ns; +}; + +/** + * struct i2c_bus_recovery_info - I2C bus recovery information + * @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or + * i2c_generic_scl_recovery(). + * @get_scl: This gets current value of SCL line. Mandatory for generic SCL + * recovery. Populated internally for generic GPIO recovery. + * @set_scl: This sets/clears the SCL line. Mandatory for generic SCL recovery. + * Populated internally for generic GPIO recovery. + * @get_sda: This gets current value of SDA line. This or set_sda() is mandatory + * for generic SCL recovery. Populated internally, if sda_gpio is a valid + * GPIO, for generic GPIO recovery. + * @set_sda: This sets/clears the SDA line. This or get_sda() is mandatory for + * generic SCL recovery. Populated internally, if sda_gpio is a valid GPIO, + * for generic GPIO recovery. + * @get_bus_free: Returns the bus free state as seen from the IP core in case it + * has a more complex internal logic than just reading SDA. Optional. + * @prepare_recovery: This will be called before starting recovery. Platform may + * configure padmux here for SDA/SCL line or something else they want. + * @unprepare_recovery: This will be called after completing recovery. Platform + * may configure padmux here for SDA/SCL line or something else they want. + * @scl_gpiod: gpiod of the SCL line. Only required for GPIO recovery. + * @sda_gpiod: gpiod of the SDA line. Only required for GPIO recovery. + */ +struct i2c_bus_recovery_info { + int (*recover_bus)(struct i2c_adapter *adap); + + int (*get_scl)(struct i2c_adapter *adap); + void (*set_scl)(struct i2c_adapter *adap, int val); + int (*get_sda)(struct i2c_adapter *adap); + void (*set_sda)(struct i2c_adapter *adap, int val); + int (*get_bus_free)(struct i2c_adapter *adap); + + void (*prepare_recovery)(struct i2c_adapter *adap); + void (*unprepare_recovery)(struct i2c_adapter *adap); + + /* gpio recovery */ + struct gpio_desc *scl_gpiod; + struct gpio_desc *sda_gpiod; +}; + +int i2c_recover_bus(struct i2c_adapter *adap); + +/* Generic recovery routines */ +int i2c_generic_scl_recovery(struct i2c_adapter *adap); + +/** + * struct i2c_adapter_quirks - describe flaws of an i2c adapter + * @flags: see I2C_AQ_* for possible flags and read below + * @max_num_msgs: maximum number of messages per transfer + * @max_write_len: maximum length of a write message + * @max_read_len: maximum length of a read message + * @max_comb_1st_msg_len: maximum length of the first msg in a combined message + * @max_comb_2nd_msg_len: maximum length of the second msg in a combined message + * + * Note about combined messages: Some I2C controllers can only send one message + * per transfer, plus something called combined message or write-then-read. + * This is (usually) a small write message followed by a read message and + * barely enough to access register based devices like EEPROMs. There is a flag + * to support this mode. It implies max_num_msg = 2 and does the length checks + * with max_comb_*_len because combined message mode usually has its own + * limitations. Because of HW implementations, some controllers can actually do + * write-then-anything or other variants. To support that, write-then-read has + * been broken out into smaller bits like write-first and read-second which can + * be combined as needed. + */ + +struct i2c_adapter_quirks { + u64 flags; + int max_num_msgs; + u16 max_write_len; + u16 max_read_len; + u16 max_comb_1st_msg_len; + u16 max_comb_2nd_msg_len; +}; + +/* enforce max_num_msgs = 2 and use max_comb_*_len for length checks */ +#define I2C_AQ_COMB BIT(0) +/* first combined message must be write */ +#define I2C_AQ_COMB_WRITE_FIRST BIT(1) +/* second combined message must be read */ +#define I2C_AQ_COMB_READ_SECOND BIT(2) +/* both combined messages must have the same target address */ +#define I2C_AQ_COMB_SAME_ADDR BIT(3) +/* convenience macro for typical write-then read case */ +#define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \ + I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR) +/* clock stretching is not supported */ +#define I2C_AQ_NO_CLK_STRETCH BIT(4) +/* message cannot have length of 0 */ +#define I2C_AQ_NO_ZERO_LEN_READ BIT(5) +#define I2C_AQ_NO_ZERO_LEN_WRITE BIT(6) +#define I2C_AQ_NO_ZERO_LEN (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE) + +/* + * i2c_adapter is the structure used to identify a physical i2c bus along + * with the access algorithms necessary to access it. + */ +struct i2c_adapter { + struct module *owner; + unsigned int class; /* classes to allow probing for */ + const struct i2c_algorithm *algo; /* the algorithm to access the bus */ + void *algo_data; + + /* data fields that are valid for all devices */ + const struct i2c_lock_operations *lock_ops; + struct rt_mutex bus_lock; + struct rt_mutex mux_lock; + + int timeout; /* in jiffies */ + int retries; + struct device dev; /* the adapter device */ + unsigned long locked_flags; /* owned by the I2C core */ +#define I2C_ALF_IS_SUSPENDED 0 +#define I2C_ALF_SUSPEND_REPORTED 1 + + int nr; + char name[48]; + struct completion dev_released; + + struct mutex userspace_clients_lock; + struct list_head userspace_clients; + + struct i2c_bus_recovery_info *bus_recovery_info; + const struct i2c_adapter_quirks *quirks; + + struct irq_domain *host_notify_domain; +}; +#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) + +static inline void *i2c_get_adapdata(const struct i2c_adapter *adap) +{ + return dev_get_drvdata(&adap->dev); +} + +static inline void i2c_set_adapdata(struct i2c_adapter *adap, void *data) +{ + dev_set_drvdata(&adap->dev, data); +} + +static inline struct i2c_adapter * +i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) +{ +#if IS_ENABLED(CONFIG_I2C_MUX) + struct device *parent = adapter->dev.parent; + + if (parent != NULL && parent->type == &i2c_adapter_type) + return to_i2c_adapter(parent); + else +#endif + return NULL; +} + +int i2c_for_each_dev(void *data, int (*fn)(struct device *dev, void *data)); + +/* Adapter locking functions, exported for shared pin cases */ +#define I2C_LOCK_ROOT_ADAPTER BIT(0) +#define I2C_LOCK_SEGMENT BIT(1) + +/** + * i2c_lock_bus - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree + */ +static inline void +i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + adapter->lock_ops->lock_bus(adapter, flags); +} + +/** + * i2c_trylock_bus - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER tries to locks the root i2c adapter, + * I2C_LOCK_SEGMENT tries to lock only this branch in the adapter tree + * + * Return: true if the I2C bus segment is locked, false otherwise + */ +static inline int +i2c_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + return adapter->lock_ops->trylock_bus(adapter, flags); +} + +/** + * i2c_unlock_bus - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree + */ +static inline void +i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + adapter->lock_ops->unlock_bus(adapter, flags); +} + +/** + * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core + * @adap: Adapter to mark as suspended + * + * When using this helper to mark an adapter as suspended, the core will reject + * further transfers to this adapter. The usage of this helper is optional but + * recommended for devices having distinct handlers for system suspend and + * runtime suspend. More complex devices are free to implement custom solutions + * to reject transfers when suspended. + */ +static inline void i2c_mark_adapter_suspended(struct i2c_adapter *adap) +{ + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + set_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); +} + +/** + * i2c_mark_adapter_resumed - Report resumed state of the adapter to the core + * @adap: Adapter to mark as resumed + * + * When using this helper to mark an adapter as resumed, the core will allow + * further transfers to this adapter. See also further notes to + * @i2c_mark_adapter_suspended(). + */ +static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap) +{ + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + clear_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags); + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); +} + +/* i2c adapter classes (bitmask) */ +#define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */ +#define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */ +#define I2C_CLASS_SPD (1<<7) /* Memory modules */ +/* Warn users that the adapter doesn't support classes anymore */ +#define I2C_CLASS_DEPRECATED (1<<8) + +/* Internal numbers to terminate lists */ +#define I2C_CLIENT_END 0xfffeU + +/* Construct an I2C_CLIENT_END-terminated array of i2c addresses */ +#define I2C_ADDRS(addr, addrs...) \ + ((const unsigned short []){ addr, ## addrs, I2C_CLIENT_END }) + + +/* ----- functions exported by i2c.o */ + +/* administration... + */ +#if IS_ENABLED(CONFIG_I2C) +extern int i2c_add_adapter(struct i2c_adapter *adap); +extern void i2c_del_adapter(struct i2c_adapter *adap); +extern int i2c_add_numbered_adapter(struct i2c_adapter *adap); + +extern int i2c_register_driver(struct module *owner, struct i2c_driver *driver); +extern void i2c_del_driver(struct i2c_driver *driver); + +/* use a define to avoid include chaining to get THIS_MODULE */ +#define i2c_add_driver(driver) \ + i2c_register_driver(THIS_MODULE, driver) + +extern struct i2c_client *i2c_use_client(struct i2c_client *client); +extern void i2c_release_client(struct i2c_client *client); + +/* call the i2c_client->command() of all attached clients with + * the given arguments */ +extern void i2c_clients_command(struct i2c_adapter *adap, + unsigned int cmd, void *arg); + +extern struct i2c_adapter *i2c_get_adapter(int nr); +extern void i2c_put_adapter(struct i2c_adapter *adap); +extern unsigned int i2c_adapter_depth(struct i2c_adapter *adapter); + +void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults); + +/* Return the functionality mask */ +static inline u32 i2c_get_functionality(struct i2c_adapter *adap) +{ + return adap->algo->functionality(adap); +} + +/* Return 1 if adapter supports everything we need, 0 if not. */ +static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) +{ + return (func & i2c_get_functionality(adap)) == func; +} + +/** + * i2c_check_quirks() - Function for checking the quirk flags in an i2c adapter + * @adap: i2c adapter + * @quirks: quirk flags + * + * Return: true if the adapter has all the specified quirk flags, false if not + */ +static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks) +{ + if (!adap->quirks) + return false; + return (adap->quirks->flags & quirks) == quirks; +} + +/* Return the adapter number for a specific adapter */ +static inline int i2c_adapter_id(struct i2c_adapter *adap) +{ + return adap->nr; +} + +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) +{ + return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); +} + +u8 *i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold); +void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred); + +int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr); +/** + * module_i2c_driver() - Helper macro for registering a modular I2C driver + * @__i2c_driver: i2c_driver struct + * + * Helper macro for I2C drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_i2c_driver(__i2c_driver) \ + module_driver(__i2c_driver, i2c_add_driver, \ + i2c_del_driver) + +/** + * builtin_i2c_driver() - Helper macro for registering a builtin I2C driver + * @__i2c_driver: i2c_driver struct + * + * Helper macro for I2C drivers which do not do anything special in their + * init. This eliminates a lot of boilerplate. Each driver may only + * use this macro once, and calling it replaces device_initcall(). + */ +#define builtin_i2c_driver(__i2c_driver) \ + builtin_driver(__i2c_driver, i2c_add_driver) + +#endif /* I2C */ + +#if IS_ENABLED(CONFIG_OF) +/* must call put_device() when done with returned i2c_client device */ +extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); + +/* must call put_device() when done with returned i2c_adapter device */ +extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node); + +/* must call i2c_put_adapter() when done with returned i2c_adapter device */ +struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node); + +extern const struct of_device_id +*i2c_of_match_device(const struct of_device_id *matches, + struct i2c_client *client); + +int of_i2c_get_board_info(struct device *dev, struct device_node *node, + struct i2c_board_info *info); + +#else + +static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) +{ + return NULL; +} + +static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) +{ + return NULL; +} + +static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) +{ + return NULL; +} + +static inline const struct of_device_id +*i2c_of_match_device(const struct of_device_id *matches, + struct i2c_client *client) +{ + return NULL; +} + +static inline int of_i2c_get_board_info(struct device *dev, + struct device_node *node, + struct i2c_board_info *info) +{ + return -ENOTSUPP; +} + +#endif /* CONFIG_OF */ + +struct acpi_resource; +struct acpi_resource_i2c_serialbus; + +#if IS_ENABLED(CONFIG_ACPI) +bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c); +u32 i2c_acpi_find_bus_speed(struct device *dev); +struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, + struct i2c_board_info *info); +struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle); +#else +static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares, + struct acpi_resource_i2c_serialbus **i2c) +{ + return false; +} +static inline u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + return 0; +} +static inline struct i2c_client *i2c_acpi_new_device(struct device *dev, + int index, struct i2c_board_info *info) +{ + return NULL; +} +static inline struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) +{ + return NULL; +} +#endif /* CONFIG_ACPI */ + +#endif /* _LINUX_I2C_H */ diff --git a/t/tree/include/linux/key.h b/t/tree/include/linux/key.h new file mode 100644 index 00000000..6cf8e71c --- /dev/null +++ b/t/tree/include/linux/key.h @@ -0,0 +1,500 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Authentication token and access key management + * + * Copyright (C) 2004, 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * See Documentation/security/keys/core.rst for information on keys/keyrings. + */ + +#ifndef _LINUX_KEY_H +#define _LINUX_KEY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __KERNEL__ +#include + +/* key handle serial number */ +typedef int32_t key_serial_t; + +/* key handle permissions mask */ +typedef uint32_t key_perm_t; + +struct key; +struct net; + +#ifdef CONFIG_KEYS + +#undef KEY_DEBUGGING + +#define KEY_POS_VIEW 0x01000000 /* possessor can view a key's attributes */ +#define KEY_POS_READ 0x02000000 /* possessor can read key payload / view keyring */ +#define KEY_POS_WRITE 0x04000000 /* possessor can update key payload / add link to keyring */ +#define KEY_POS_SEARCH 0x08000000 /* possessor can find a key in search / search a keyring */ +#define KEY_POS_LINK 0x10000000 /* possessor can create a link to a key/keyring */ +#define KEY_POS_SETATTR 0x20000000 /* possessor can set key attributes */ +#define KEY_POS_ALL 0x3f000000 + +#define KEY_USR_VIEW 0x00010000 /* user permissions... */ +#define KEY_USR_READ 0x00020000 +#define KEY_USR_WRITE 0x00040000 +#define KEY_USR_SEARCH 0x00080000 +#define KEY_USR_LINK 0x00100000 +#define KEY_USR_SETATTR 0x00200000 +#define KEY_USR_ALL 0x003f0000 + +#define KEY_GRP_VIEW 0x00000100 /* group permissions... */ +#define KEY_GRP_READ 0x00000200 +#define KEY_GRP_WRITE 0x00000400 +#define KEY_GRP_SEARCH 0x00000800 +#define KEY_GRP_LINK 0x00001000 +#define KEY_GRP_SETATTR 0x00002000 +#define KEY_GRP_ALL 0x00003f00 + +#define KEY_OTH_VIEW 0x00000001 /* third party permissions... */ +#define KEY_OTH_READ 0x00000002 +#define KEY_OTH_WRITE 0x00000004 +#define KEY_OTH_SEARCH 0x00000008 +#define KEY_OTH_LINK 0x00000010 +#define KEY_OTH_SETATTR 0x00000020 +#define KEY_OTH_ALL 0x0000003f + +#define KEY_PERM_UNDEF 0xffffffff + +struct seq_file; +struct user_struct; +struct signal_struct; +struct cred; + +struct key_type; +struct key_owner; +struct key_tag; +struct keyring_list; +struct keyring_name; + +struct key_tag { + struct rcu_head rcu; + refcount_t usage; + bool removed; /* T when subject removed */ +}; + +struct keyring_index_key { + /* [!] If this structure is altered, the union in struct key must change too! */ + unsigned long hash; /* Hash value */ + union { + struct { +#ifdef __LITTLE_ENDIAN /* Put desc_len at the LSB of x */ + u16 desc_len; + char desc[sizeof(long) - 2]; /* First few chars of description */ +#else + char desc[sizeof(long) - 2]; /* First few chars of description */ + u16 desc_len; +#endif + }; + unsigned long x; + }; + struct key_type *type; + struct key_tag *domain_tag; /* Domain of operation */ + const char *description; +}; + +union key_payload { + void __rcu *rcu_data0; + void *data[4]; +}; + +/*****************************************************************************/ +/* + * key reference with possession attribute handling + * + * NOTE! key_ref_t is a typedef'd pointer to a type that is not actually + * defined. This is because we abuse the bottom bit of the reference to carry a + * flag to indicate whether the calling process possesses that key in one of + * its keyrings. + * + * the key_ref_t has been made a separate type so that the compiler can reject + * attempts to dereference it without proper conversion. + * + * the three functions are used to assemble and disassemble references + */ +typedef struct __key_reference_with_attributes *key_ref_t; + +static inline key_ref_t make_key_ref(const struct key *key, + bool possession) +{ + return (key_ref_t) ((unsigned long) key | possession); +} + +static inline struct key *key_ref_to_ptr(const key_ref_t key_ref) +{ + return (struct key *) ((unsigned long) key_ref & ~1UL); +} + +static inline bool is_key_possessed(const key_ref_t key_ref) +{ + return (unsigned long) key_ref & 1UL; +} + +typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *restriction_key); + +struct key_restriction { + key_restrict_link_func_t check; + struct key *key; + struct key_type *keytype; +}; + +enum key_state { + KEY_IS_UNINSTANTIATED, + KEY_IS_POSITIVE, /* Positively instantiated */ +}; + +/*****************************************************************************/ +/* + * authentication token / access credential / keyring + * - types of key include: + * - keyrings + * - disk encryption IDs + * - Kerberos TGTs and tickets + */ +struct key { + refcount_t usage; /* number of references */ + key_serial_t serial; /* key serial number */ + union { + struct list_head graveyard_link; + struct rb_node serial_node; + }; + struct rw_semaphore sem; /* change vs change sem */ + struct key_user *user; /* owner of this key */ + void *security; /* security data for this key */ + union { + time64_t expiry; /* time at which key expires (or 0) */ + time64_t revoked_at; /* time at which key was revoked */ + }; + time64_t last_used_at; /* last time used for LRU keyring discard */ + kuid_t uid; + kgid_t gid; + key_perm_t perm; /* access permissions */ + unsigned short quotalen; /* length added to quota */ + unsigned short datalen; /* payload data length + * - may not match RCU dereferenced payload + * - payload should contain own length + */ + short state; /* Key state (+) or rejection error (-) */ + +#ifdef KEY_DEBUGGING + unsigned magic; +#define KEY_DEBUG_MAGIC 0x18273645u +#endif + + unsigned long flags; /* status flags (change with bitops) */ +#define KEY_FLAG_DEAD 0 /* set if key type has been deleted */ +#define KEY_FLAG_REVOKED 1 /* set if key had been revoked */ +#define KEY_FLAG_IN_QUOTA 2 /* set if key consumes quota */ +#define KEY_FLAG_USER_CONSTRUCT 3 /* set if key is being constructed in userspace */ +#define KEY_FLAG_ROOT_CAN_CLEAR 4 /* set if key can be cleared by root without permission */ +#define KEY_FLAG_INVALIDATED 5 /* set if key has been invalidated */ +#define KEY_FLAG_BUILTIN 6 /* set if key is built in to the kernel */ +#define KEY_FLAG_ROOT_CAN_INVAL 7 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 8 /* set if key should not be removed */ +#define KEY_FLAG_UID_KEYRING 9 /* set if key is a user or user session keyring */ + + /* the key type and key description string + * - the desc is used to match a key against search criteria + * - it should be a printable string + * - eg: for krb5 AFS, this might be "afs@REDHAT.COM" + */ + union { + struct keyring_index_key index_key; + struct { + unsigned long hash; + unsigned long len_desc; + struct key_type *type; /* type of key */ + struct key_tag *domain_tag; /* Domain of operation */ + char *description; + }; + }; + + /* key data + * - this is used to hold the data actually used in cryptography or + * whatever + */ + union { + union key_payload payload; + struct { + /* Keyring bits */ + struct list_head name_link; + struct assoc_array keys; + }; + }; + + /* This is set on a keyring to restrict the addition of a link to a key + * to it. If this structure isn't provided then it is assumed that the + * keyring is open to any addition. It is ignored for non-keyring + * keys. Only set this value using keyring_restrict(), keyring_alloc(), + * or key_alloc(). + * + * This is intended for use with rings of trusted keys whereby addition + * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION + * overrides this, allowing the kernel to add extra keys without + * restriction. + */ + struct key_restriction *restrict_link; +}; + +extern struct key *key_alloc(struct key_type *type, + const char *desc, + kuid_t uid, kgid_t gid, + const struct cred *cred, + key_perm_t perm, + unsigned long flags, + struct key_restriction *restrict_link); + + +#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ +#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ +#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ +#define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */ +#define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */ +#define KEY_ALLOC_UID_KEYRING 0x0010 /* allocating a user or user session keyring */ + +extern void key_revoke(struct key *key); +extern void key_invalidate(struct key *key); +extern void key_put(struct key *key); +extern bool key_put_tag(struct key_tag *tag); +extern void key_remove_domain(struct key_tag *domain_tag); + +static inline struct key *__key_get(struct key *key) +{ + refcount_inc(&key->usage); + return key; +} + +static inline struct key *key_get(struct key *key) +{ + return key ? __key_get(key) : key; +} + +static inline void key_ref_put(key_ref_t key_ref) +{ + key_put(key_ref_to_ptr(key_ref)); +} + +extern struct key *request_key_tag(struct key_type *type, + const char *description, + struct key_tag *domain_tag, + const char *callout_info); + +extern struct key *request_key_rcu(struct key_type *type, + const char *description, + struct key_tag *domain_tag); + +extern struct key *request_key_with_auxdata(struct key_type *type, + const char *description, + struct key_tag *domain_tag, + const void *callout_info, + size_t callout_len, + void *aux); + +/** + * request_key - Request a key and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key_tag(), but with the default global domain tag. + */ +static inline struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info) +{ + return request_key_tag(type, description, NULL, callout_info); +} + +#ifdef CONFIG_NET +/** + * request_key_net - Request a key for a net namespace and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @net: The network namespace that is the key's domain of operation. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key() except that it does not add the returned key to a + * keyring if found, new keys are always allocated in the user's quota, the + * callout_info must be a NUL-terminated string and no auxiliary data can be + * passed. Only keys that operate the specified network namespace are used. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait. + */ +#define request_key_net(type, description, net, callout_info) \ + request_key_tag(type, description, net->key_domain, callout_info); + +/** + * request_key_net_rcu - Request a key for a net namespace under RCU conditions + * @type: Type of key. + * @description: The searchable description of the key. + * @net: The network namespace that is the key's domain of operation. + * + * As for request_key_rcu() except that only keys that operate the specified + * network namespace are used. + */ +#define request_key_net_rcu(type, description, net) \ + request_key_rcu(type, description, net->key_domain); +#endif /* CONFIG_NET */ + +extern int wait_for_key_construction(struct key *key, bool intr); + +extern int key_validate(const struct key *key); + +extern key_ref_t key_create_or_update(key_ref_t keyring, + const char *type, + const char *description, + const void *payload, + size_t plen, + key_perm_t perm, + unsigned long flags); + +extern int key_update(key_ref_t key, + const void *payload, + size_t plen); + +extern int key_link(struct key *keyring, + struct key *key); + +extern int key_move(struct key *key, + struct key *from_keyring, + struct key *to_keyring, + unsigned int flags); + +extern int key_unlink(struct key *keyring, + struct key *key); + +extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, + const struct cred *cred, + key_perm_t perm, + unsigned long flags, + struct key_restriction *restrict_link, + struct key *dest); + +extern int restrict_link_reject(struct key *keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *restriction_key); + +extern int keyring_clear(struct key *keyring); + +extern key_ref_t keyring_search(key_ref_t keyring, + struct key_type *type, + const char *description, + bool recurse); + +extern int keyring_add_key(struct key *keyring, + struct key *key); + +extern int keyring_restrict(key_ref_t keyring, const char *type, + const char *restriction); + +extern struct key *key_lookup(key_serial_t id); + +static inline key_serial_t key_serial(const struct key *key) +{ + return key ? key->serial : 0; +} + +extern void key_set_timeout(struct key *, unsigned); + +extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, + key_perm_t perm); +extern void key_free_user_ns(struct user_namespace *); + +/* + * The permissions required on a key that we're looking up. + */ +#define KEY_NEED_VIEW 0x01 /* Require permission to view attributes */ +#define KEY_NEED_READ 0x02 /* Require permission to read content */ +#define KEY_NEED_WRITE 0x04 /* Require permission to update / modify */ +#define KEY_NEED_SEARCH 0x08 /* Require permission to search (keyring) or find (key) */ +#define KEY_NEED_LINK 0x10 /* Require permission to link */ +#define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */ +#define KEY_NEED_ALL 0x3f /* All the above permissions */ + +static inline short key_read_state(const struct key *key) +{ + /* Barrier versus mark_key_instantiated(). */ + return smp_load_acquire(&key->state); +} + +/** + * key_is_positive - Determine if a key has been positively instantiated + * @key: The key to check. + * + * Return true if the specified key has been positively instantiated, false + * otherwise. + */ +static inline bool key_is_positive(const struct key *key) +{ + return key_read_state(key) == KEY_IS_POSITIVE; +} + +static inline bool key_is_negative(const struct key *key) +{ + return key_read_state(key) < 0; +} + +#define dereference_key_rcu(KEY) \ + (rcu_dereference((KEY)->payload.rcu_data0)) + +#define dereference_key_locked(KEY) \ + (rcu_dereference_protected((KEY)->payload.rcu_data0, \ + rwsem_is_locked(&((struct key *)(KEY))->sem))) + +#define rcu_assign_keypointer(KEY, PAYLOAD) \ +do { \ + rcu_assign_pointer((KEY)->payload.rcu_data0, (PAYLOAD)); \ +} while (0) + +#ifdef CONFIG_SYSCTL +extern struct ctl_table key_sysctls[]; +#endif +/* + * the userspace interface + */ +extern int install_thread_keyring_to_cred(struct cred *cred); +extern void key_fsuid_changed(struct cred *new_cred); +extern void key_fsgid_changed(struct cred *new_cred); +extern void key_init(void); + +#else /* CONFIG_KEYS */ + +#define key_validate(k) 0 +#define key_serial(k) 0 +#define key_get(k) ({ NULL; }) +#define key_revoke(k) do { } while(0) +#define key_invalidate(k) do { } while(0) +#define key_put(k) do { } while(0) +#define key_ref_put(k) do { } while(0) +#define make_key_ref(k, p) NULL +#define key_ref_to_ptr(k) NULL +#define is_key_possessed(k) 0 +#define key_fsuid_changed(c) do { } while(0) +#define key_fsgid_changed(c) do { } while(0) +#define key_init() do { } while(0) +#define key_free_user_ns(ns) do { } while(0) +#define key_remove_domain(d) do { } while(0) + +#endif /* CONFIG_KEYS */ +#endif /* __KERNEL__ */ +#endif /* _LINUX_KEY_H */ diff --git a/t/tree/include/linux/kmod.h b/t/tree/include/linux/kmod.h new file mode 100644 index 00000000..68f69362 --- /dev/null +++ b/t/tree/include/linux/kmod.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __LINUX_KMOD_H__ +#define __LINUX_KMOD_H__ + +/* + * include/linux/kmod.h + */ + +#include +#include +#include +#include +#include +#include +#include + +#define KMOD_PATH_LEN 256 + +#ifdef CONFIG_MODULES +extern char modprobe_path[]; /* for sysctl */ +/* modprobe exit status on success, -ve on error. Return value + * usually useless though. */ +extern __printf(2, 3) +int __request_module(bool wait, const char *name, ...); +#define request_module(mod...) __request_module(true, mod) +#define request_module_nowait(mod...) __request_module(false, mod) +#define try_then_request_module(x, mod...) \ + ((x) ?: (__request_module(true, mod), (x))) +#else +static inline int request_module(const char *name, ...) { return -ENOSYS; } +static inline int request_module_nowait(const char *name, ...) { return -ENOSYS; } +#define try_then_request_module(x, mod...) (x) +#endif + +#endif /* __LINUX_KMOD_H__ */ diff --git a/t/tree/include/linux/log2.h b/t/tree/include/linux/log2.h new file mode 100644 index 00000000..83a4a3ca --- /dev/null +++ b/t/tree/include/linux/log2.h @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Integer base 2 logarithm calculation + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _LINUX_LOG2_H +#define _LINUX_LOG2_H + +#include +#include + +/* + * non-constant log of base 2 calculators + * - the arch may override these in asm/bitops.h if they can be implemented + * more efficiently than using fls() and fls64() + * - the arch is not required to handle n==0 if implementing the fallback + */ +#ifndef CONFIG_ARCH_HAS_ILOG2_U32 +static inline __attribute__((const)) +int __ilog2_u32(u32 n) +{ + return fls(n) - 1; +} +#endif + +#ifndef CONFIG_ARCH_HAS_ILOG2_U64 +static inline __attribute__((const)) +int __ilog2_u64(u64 n) +{ + return fls64(n) - 1; +} +#endif + +/** + * is_power_of_2() - check if a value is a power of two + * @n: the value to check + * + * Determine whether some value is a power of two, where zero is + * *not* considered a power of two. + * Return: true if @n is a power of 2, otherwise false. + */ +static inline __attribute__((const)) +bool is_power_of_2(unsigned long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +/** + * __roundup_pow_of_two() - round up to nearest power of two + * @n: value to round up + */ +static inline __attribute__((const)) +unsigned long __roundup_pow_of_two(unsigned long n) +{ + return 1UL << fls_long(n - 1); +} + +/** + * __rounddown_pow_of_two() - round down to nearest power of two + * @n: value to round down + */ +static inline __attribute__((const)) +unsigned long __rounddown_pow_of_two(unsigned long n) +{ + return 1UL << (fls_long(n) - 1); +} + +/** + * const_ilog2 - log base 2 of 32-bit or a 64-bit constant unsigned value + * @n: parameter + * + * Use this where sparse expects a true constant expression, e.g. for array + * indices. + */ +#define const_ilog2(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n) < 2 ? 0 : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ + (n) & (1ULL << 60) ? 60 : \ + (n) & (1ULL << 59) ? 59 : \ + (n) & (1ULL << 58) ? 58 : \ + (n) & (1ULL << 57) ? 57 : \ + (n) & (1ULL << 56) ? 56 : \ + (n) & (1ULL << 55) ? 55 : \ + (n) & (1ULL << 54) ? 54 : \ + (n) & (1ULL << 53) ? 53 : \ + (n) & (1ULL << 52) ? 52 : \ + (n) & (1ULL << 51) ? 51 : \ + (n) & (1ULL << 50) ? 50 : \ + (n) & (1ULL << 49) ? 49 : \ + (n) & (1ULL << 48) ? 48 : \ + (n) & (1ULL << 47) ? 47 : \ + (n) & (1ULL << 46) ? 46 : \ + (n) & (1ULL << 45) ? 45 : \ + (n) & (1ULL << 44) ? 44 : \ + (n) & (1ULL << 43) ? 43 : \ + (n) & (1ULL << 42) ? 42 : \ + (n) & (1ULL << 41) ? 41 : \ + (n) & (1ULL << 40) ? 40 : \ + (n) & (1ULL << 39) ? 39 : \ + (n) & (1ULL << 38) ? 38 : \ + (n) & (1ULL << 37) ? 37 : \ + (n) & (1ULL << 36) ? 36 : \ + (n) & (1ULL << 35) ? 35 : \ + (n) & (1ULL << 34) ? 34 : \ + (n) & (1ULL << 33) ? 33 : \ + (n) & (1ULL << 32) ? 32 : \ + (n) & (1ULL << 31) ? 31 : \ + (n) & (1ULL << 30) ? 30 : \ + (n) & (1ULL << 29) ? 29 : \ + (n) & (1ULL << 28) ? 28 : \ + (n) & (1ULL << 27) ? 27 : \ + (n) & (1ULL << 26) ? 26 : \ + (n) & (1ULL << 25) ? 25 : \ + (n) & (1ULL << 24) ? 24 : \ + (n) & (1ULL << 23) ? 23 : \ + (n) & (1ULL << 22) ? 22 : \ + (n) & (1ULL << 21) ? 21 : \ + (n) & (1ULL << 20) ? 20 : \ + (n) & (1ULL << 19) ? 19 : \ + (n) & (1ULL << 18) ? 18 : \ + (n) & (1ULL << 17) ? 17 : \ + (n) & (1ULL << 16) ? 16 : \ + (n) & (1ULL << 15) ? 15 : \ + (n) & (1ULL << 14) ? 14 : \ + (n) & (1ULL << 13) ? 13 : \ + (n) & (1ULL << 12) ? 12 : \ + (n) & (1ULL << 11) ? 11 : \ + (n) & (1ULL << 10) ? 10 : \ + (n) & (1ULL << 9) ? 9 : \ + (n) & (1ULL << 8) ? 8 : \ + (n) & (1ULL << 7) ? 7 : \ + (n) & (1ULL << 6) ? 6 : \ + (n) & (1ULL << 5) ? 5 : \ + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ + 1) : \ + -1) + +/** + * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value + * @n: parameter + * + * constant-capable log of base 2 calculation + * - this can be used to initialise global variables from constant data, hence + * the massive ternary operator construction + * + * selects the appropriately-sized optimised version depending on sizeof(n) + */ +#define ilog2(n) \ +( \ + __builtin_constant_p(n) ? \ + const_ilog2(n) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ + ) + +/** + * roundup_pow_of_two - round the given value up to nearest power of two + * @n: parameter + * + * round the given value up to the nearest power of two + * - the result is undefined when n == 0 + * - this can be used to initialise global variables from constant data + */ +#define roundup_pow_of_two(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (n == 1) ? 1 : \ + (1UL << (ilog2((n) - 1) + 1)) \ + ) : \ + __roundup_pow_of_two(n) \ + ) + +/** + * rounddown_pow_of_two - round the given value down to nearest power of two + * @n: parameter + * + * round the given value down to the nearest power of two + * - the result is undefined when n == 0 + * - this can be used to initialise global variables from constant data + */ +#define rounddown_pow_of_two(n) \ +( \ + __builtin_constant_p(n) ? ( \ + (1UL << ilog2(n))) : \ + __rounddown_pow_of_two(n) \ + ) + +static inline __attribute_const__ +int __order_base_2(unsigned long n) +{ + return n > 1 ? ilog2(n - 1) + 1 : 0; +} + +/** + * order_base_2 - calculate the (rounded up) base 2 order of the argument + * @n: parameter + * + * The first few values calculated by this routine: + * ob2(0) = 0 + * ob2(1) = 0 + * ob2(2) = 1 + * ob2(3) = 2 + * ob2(4) = 2 + * ob2(5) = 3 + * ... and so on. + */ +#define order_base_2(n) \ +( \ + __builtin_constant_p(n) ? ( \ + ((n) == 0 || (n) == 1) ? 0 : \ + ilog2((n) - 1) + 1) : \ + __order_base_2(n) \ +) + +static inline __attribute__((const)) +int __bits_per(unsigned long n) +{ + if (n < 2) + return 1; + if (is_power_of_2(n)) + return order_base_2(n) + 1; + return order_base_2(n); +} + +/** + * bits_per - calculate the number of bits required for the argument + * @n: parameter + * + * This is constant-capable and can be used for compile time + * initializations, e.g bitfields. + * + * The first few values calculated by this routine: + * bf(0) = 1 + * bf(1) = 1 + * bf(2) = 2 + * bf(3) = 2 + * bf(4) = 3 + * ... and so on. + */ +#define bits_per(n) \ +( \ + __builtin_constant_p(n) ? ( \ + ((n) == 0 || (n) == 1) \ + ? 1 : ilog2(n) + 1 \ + ) : \ + __bits_per(n) \ +) +#endif /* _LINUX_LOG2_H */ diff --git a/t/tree/include/linux/logic_pio.h b/t/tree/include/linux/logic_pio.h new file mode 100644 index 00000000..88e1e630 --- /dev/null +++ b/t/tree/include/linux/logic_pio.h @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved. + * Author: Gabriele Paoloni + * Author: Zhichang Yuan + */ + +#ifndef __LINUX_LOGIC_PIO_H +#define __LINUX_LOGIC_PIO_H + +#include + +enum { + LOGIC_PIO_INDIRECT, /* Indirect IO flag */ + LOGIC_PIO_CPU_MMIO, /* Memory-mapped IO flag */ +}; + +struct logic_pio_hwaddr { + struct list_head list; + struct fwnode_handle *fwnode; + resource_size_t hw_start; + resource_size_t io_start; + resource_size_t size; /* range size populated */ + unsigned long flags; + + void *hostdata; + const struct logic_pio_host_ops *ops; +}; + +struct logic_pio_host_ops { + u32 (*in)(void *hostdata, unsigned long addr, size_t dwidth); + void (*out)(void *hostdata, unsigned long addr, u32 val, + size_t dwidth); + u32 (*ins)(void *hostdata, unsigned long addr, void *buffer, + size_t dwidth, unsigned int count); + void (*outs)(void *hostdata, unsigned long addr, const void *buffer, + size_t dwidth, unsigned int count); +}; + +#ifdef CONFIG_INDIRECT_PIO +u8 logic_inb(unsigned long addr); +void logic_outb(u8 value, unsigned long addr); +void logic_outw(u16 value, unsigned long addr); +void logic_outl(u32 value, unsigned long addr); +u16 logic_inw(unsigned long addr); +u32 logic_inl(unsigned long addr); +void logic_outb(u8 value, unsigned long addr); +void logic_outw(u16 value, unsigned long addr); +void logic_outl(u32 value, unsigned long addr); +void logic_insb(unsigned long addr, void *buffer, unsigned int count); +void logic_insl(unsigned long addr, void *buffer, unsigned int count); +void logic_insw(unsigned long addr, void *buffer, unsigned int count); +void logic_outsb(unsigned long addr, const void *buffer, unsigned int count); +void logic_outsw(unsigned long addr, const void *buffer, unsigned int count); +void logic_outsl(unsigned long addr, const void *buffer, unsigned int count); + +#ifndef inb +#define inb logic_inb +#endif + +#ifndef inw +#define inw logic_inw +#endif + +#ifndef inl +#define inl logic_inl +#endif + +#ifndef outb +#define outb logic_outb +#endif + +#ifndef outw +#define outw logic_outw +#endif + +#ifndef outl +#define outl logic_outl +#endif + +#ifndef insb +#define insb logic_insb +#endif + +#ifndef insw +#define insw logic_insw +#endif + +#ifndef insl +#define insl logic_insl +#endif + +#ifndef outsb +#define outsb logic_outsb +#endif + +#ifndef outsw +#define outsw logic_outsw +#endif + +#ifndef outsl +#define outsl logic_outsl +#endif + +/* + * We reserve 0x4000 bytes for Indirect IO as so far this library is only + * used by the HiSilicon LPC Host. If needed, we can reserve a wider IO + * area by redefining the macro below. + */ +#define PIO_INDIRECT_SIZE 0x4000 +#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE) +#else +#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT +#endif /* CONFIG_INDIRECT_PIO */ + +struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode); +unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode, + resource_size_t hw_addr, resource_size_t size); +int logic_pio_register_range(struct logic_pio_hwaddr *newrange); +void logic_pio_unregister_range(struct logic_pio_hwaddr *range); +resource_size_t logic_pio_to_hwaddr(unsigned long pio); +unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr); + +#endif /* __LINUX_LOGIC_PIO_H */ diff --git a/t/tree/include/linux/of.h b/t/tree/include/linux/of.h new file mode 100644 index 00000000..844f89e1 --- /dev/null +++ b/t/tree/include/linux/of.h @@ -0,0 +1,1480 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef _LINUX_OF_H +#define _LINUX_OF_H +/* + * Definitions for talking to the Open Firmware PROM on + * Power Macintosh and other computers. + * + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. + * Updates for SPARC64 by David S. Miller + * Derived from PowerPC and Sparc prom.h files by Stephen Rothwell, IBM Corp. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef u32 phandle; +typedef u32 ihandle; + +struct property { + char *name; + int length; + void *value; + struct property *next; +#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) + unsigned long _flags; +#endif +#if defined(CONFIG_OF_PROMTREE) + unsigned int unique_id; +#endif +#if defined(CONFIG_OF_KOBJ) + struct bin_attribute attr; +#endif +}; + +#if defined(CONFIG_SPARC) +struct of_irq_controller; +#endif + +struct device_node { + const char *name; + phandle phandle; + const char *full_name; + struct fwnode_handle fwnode; + + struct property *properties; + struct property *deadprops; /* removed properties */ + struct device_node *parent; + struct device_node *child; + struct device_node *sibling; +#if defined(CONFIG_OF_KOBJ) + struct kobject kobj; +#endif + unsigned long _flags; + void *data; +#if defined(CONFIG_SPARC) + unsigned int unique_id; + struct of_irq_controller *irq_trans; +#endif +}; + +#define MAX_PHANDLE_ARGS 16 +struct of_phandle_args { + struct device_node *np; + int args_count; + uint32_t args[MAX_PHANDLE_ARGS]; +}; + +struct of_phandle_iterator { + /* Common iterator information */ + const char *cells_name; + int cell_count; + const struct device_node *parent; + + /* List size information */ + const __be32 *list_end; + const __be32 *phandle_end; + + /* Current position state */ + const __be32 *cur; + uint32_t cur_count; + phandle phandle; + struct device_node *node; +}; + +struct of_reconfig_data { + struct device_node *dn; + struct property *prop; + struct property *old_prop; +}; + +/* initialize a node */ +extern struct kobj_type of_node_ktype; +extern const struct fwnode_operations of_fwnode_ops; +static inline void of_node_init(struct device_node *node) +{ +#if defined(CONFIG_OF_KOBJ) + kobject_init(&node->kobj, &of_node_ktype); +#endif + node->fwnode.ops = &of_fwnode_ops; +} + +#if defined(CONFIG_OF_KOBJ) +#define of_node_kobj(n) (&(n)->kobj) +#else +#define of_node_kobj(n) NULL +#endif + +#ifdef CONFIG_OF_DYNAMIC +extern struct device_node *of_node_get(struct device_node *node); +extern void of_node_put(struct device_node *node); +#else /* CONFIG_OF_DYNAMIC */ +/* Dummy ref counting routines - to be implemented later */ +static inline struct device_node *of_node_get(struct device_node *node) +{ + return node; +} +static inline void of_node_put(struct device_node *node) { } +#endif /* !CONFIG_OF_DYNAMIC */ + +/* Pointer for first entry in chain of all nodes. */ +extern struct device_node *of_root; +extern struct device_node *of_chosen; +extern struct device_node *of_aliases; +extern struct device_node *of_stdout; +extern raw_spinlock_t devtree_lock; + +/* + * struct device_node flag descriptions + * (need to be visible even when !CONFIG_OF) + */ +#define OF_DYNAMIC 1 /* (and properties) allocated via kmalloc */ +#define OF_DETACHED 2 /* detached from the device tree */ +#define OF_POPULATED 3 /* device already created */ +#define OF_POPULATED_BUS 4 /* platform bus created for children */ +#define OF_OVERLAY 5 /* allocated for an overlay */ +#define OF_OVERLAY_FREE_CSET 6 /* in overlay cset being freed */ + +#define OF_BAD_ADDR ((u64)-1) + +#ifdef CONFIG_OF +void of_core_init(void); + +static inline bool is_of_node(const struct fwnode_handle *fwnode) +{ + return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &of_fwnode_ops; +} + +#define to_of_node(__fwnode) \ + ({ \ + typeof(__fwnode) __to_of_node_fwnode = (__fwnode); \ + \ + is_of_node(__to_of_node_fwnode) ? \ + container_of(__to_of_node_fwnode, \ + struct device_node, fwnode) : \ + NULL; \ + }) + +#define of_fwnode_handle(node) \ + ({ \ + typeof(node) __of_fwnode_handle_node = (node); \ + \ + __of_fwnode_handle_node ? \ + &__of_fwnode_handle_node->fwnode : NULL; \ + }) + +static inline bool of_have_populated_dt(void) +{ + return of_root != NULL; +} + +static inline bool of_node_is_root(const struct device_node *node) +{ + return node && (node->parent == NULL); +} + +static inline int of_node_check_flag(struct device_node *n, unsigned long flag) +{ + return test_bit(flag, &n->_flags); +} + +static inline int of_node_test_and_set_flag(struct device_node *n, + unsigned long flag) +{ + return test_and_set_bit(flag, &n->_flags); +} + +static inline void of_node_set_flag(struct device_node *n, unsigned long flag) +{ + set_bit(flag, &n->_flags); +} + +static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) +{ + clear_bit(flag, &n->_flags); +} + +#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) +static inline int of_property_check_flag(struct property *p, unsigned long flag) +{ + return test_bit(flag, &p->_flags); +} + +static inline void of_property_set_flag(struct property *p, unsigned long flag) +{ + set_bit(flag, &p->_flags); +} + +static inline void of_property_clear_flag(struct property *p, unsigned long flag) +{ + clear_bit(flag, &p->_flags); +} +#endif + +extern struct device_node *__of_find_all_nodes(struct device_node *prev); +extern struct device_node *of_find_all_nodes(struct device_node *prev); + +/* + * OF address retrieval & translation + */ + +/* Helper to read a big number; size is in cells (not bytes) */ +static inline u64 of_read_number(const __be32 *cell, int size) +{ + u64 r = 0; + for (; size--; cell++) + r = (r << 32) | be32_to_cpu(*cell); + return r; +} + +/* Like of_read_number, but we want an unsigned long result */ +static inline unsigned long of_read_ulong(const __be32 *cell, int size) +{ + /* toss away upper bits if unsigned long is smaller than u64 */ + return of_read_number(cell, size); +} + +#if defined(CONFIG_SPARC) +#include +#endif + +#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) +#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) + +extern bool of_node_name_eq(const struct device_node *np, const char *name); +extern bool of_node_name_prefix(const struct device_node *np, const char *prefix); + +static inline const char *of_node_full_name(const struct device_node *np) +{ + return np ? np->full_name : ""; +} + +#define for_each_of_allnodes_from(from, dn) \ + for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn)) +#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn) +extern struct device_node *of_find_node_by_name(struct device_node *from, + const char *name); +extern struct device_node *of_find_node_by_type(struct device_node *from, + const char *type); +extern struct device_node *of_find_compatible_node(struct device_node *from, + const char *type, const char *compat); +extern struct device_node *of_find_matching_node_and_match( + struct device_node *from, + const struct of_device_id *matches, + const struct of_device_id **match); + +extern struct device_node *of_find_node_opts_by_path(const char *path, + const char **opts); +static inline struct device_node *of_find_node_by_path(const char *path) +{ + return of_find_node_opts_by_path(path, NULL); +} + +extern struct device_node *of_find_node_by_phandle(phandle handle); +extern struct device_node *of_get_parent(const struct device_node *node); +extern struct device_node *of_get_next_parent(struct device_node *node); +extern struct device_node *of_get_next_child(const struct device_node *node, + struct device_node *prev); +extern struct device_node *of_get_next_available_child( + const struct device_node *node, struct device_node *prev); + +extern struct device_node *of_get_compatible_child(const struct device_node *parent, + const char *compatible); +extern struct device_node *of_get_child_by_name(const struct device_node *node, + const char *name); + +/* cache lookup */ +extern struct device_node *of_find_next_cache_node(const struct device_node *); +extern int of_find_last_cache_level(unsigned int cpu); +extern struct device_node *of_find_node_with_property( + struct device_node *from, const char *prop_name); + +extern struct property *of_find_property(const struct device_node *np, + const char *name, + int *lenp); +extern int of_property_count_elems_of_size(const struct device_node *np, + const char *propname, int elem_size); +extern int of_property_read_u32_index(const struct device_node *np, + const char *propname, + u32 index, u32 *out_value); +extern int of_property_read_u64_index(const struct device_node *np, + const char *propname, + u32 index, u64 *out_value); +extern int of_property_read_variable_u8_array(const struct device_node *np, + const char *propname, u8 *out_values, + size_t sz_min, size_t sz_max); +extern int of_property_read_variable_u16_array(const struct device_node *np, + const char *propname, u16 *out_values, + size_t sz_min, size_t sz_max); +extern int of_property_read_variable_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, + size_t sz_min, + size_t sz_max); +extern int of_property_read_u64(const struct device_node *np, + const char *propname, u64 *out_value); +extern int of_property_read_variable_u64_array(const struct device_node *np, + const char *propname, + u64 *out_values, + size_t sz_min, + size_t sz_max); + +extern int of_property_read_string(const struct device_node *np, + const char *propname, + const char **out_string); +extern int of_property_match_string(const struct device_node *np, + const char *propname, + const char *string); +extern int of_property_read_string_helper(const struct device_node *np, + const char *propname, + const char **out_strs, size_t sz, int index); +extern int of_device_is_compatible(const struct device_node *device, + const char *); +extern int of_device_compatible_match(struct device_node *device, + const char *const *compat); +extern bool of_device_is_available(const struct device_node *device); +extern bool of_device_is_big_endian(const struct device_node *device); +extern const void *of_get_property(const struct device_node *node, + const char *name, + int *lenp); +extern struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); +extern struct device_node *of_get_next_cpu_node(struct device_node *prev); + +#define for_each_property_of_node(dn, pp) \ + for (pp = dn->properties; pp != NULL; pp = pp->next) + +extern int of_n_addr_cells(struct device_node *np); +extern int of_n_size_cells(struct device_node *np); +extern const struct of_device_id *of_match_node( + const struct of_device_id *matches, const struct device_node *node); +extern int of_modalias_node(struct device_node *node, char *modalias, int len); +extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args); +extern struct device_node *of_parse_phandle(const struct device_node *np, + const char *phandle_name, + int index); +extern int of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, const char *cells_name, int index, + struct of_phandle_args *out_args); +extern int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, const char *stem_name, int index, + struct of_phandle_args *out_args); +extern int of_parse_phandle_with_fixed_args(const struct device_node *np, + const char *list_name, int cells_count, int index, + struct of_phandle_args *out_args); +extern int of_count_phandle_with_args(const struct device_node *np, + const char *list_name, const char *cells_name); + +/* phandle iterator functions */ +extern int of_phandle_iterator_init(struct of_phandle_iterator *it, + const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count); + +extern int of_phandle_iterator_next(struct of_phandle_iterator *it); +extern int of_phandle_iterator_args(struct of_phandle_iterator *it, + uint32_t *args, + int size); + +extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); +extern int of_alias_get_id(struct device_node *np, const char *stem); +extern int of_alias_get_highest_id(const char *stem); +extern int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits); + +extern int of_machine_is_compatible(const char *compat); + +extern int of_add_property(struct device_node *np, struct property *prop); +extern int of_remove_property(struct device_node *np, struct property *prop); +extern int of_update_property(struct device_node *np, struct property *newprop); + +/* For updating the device tree at runtime */ +#define OF_RECONFIG_ATTACH_NODE 0x0001 +#define OF_RECONFIG_DETACH_NODE 0x0002 +#define OF_RECONFIG_ADD_PROPERTY 0x0003 +#define OF_RECONFIG_REMOVE_PROPERTY 0x0004 +#define OF_RECONFIG_UPDATE_PROPERTY 0x0005 + +extern int of_attach_node(struct device_node *); +extern int of_detach_node(struct device_node *); + +#define of_match_ptr(_ptr) (_ptr) + +/** + * of_property_read_u8_array - Find and read an array of u8 from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_values: pointer to return value, modified only if return value is 0. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 8-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * dts entry of array should be like: + * property = /bits/ 8 <0x50 0x60 0x70>; + * + * The out_values is modified only if a valid u8 value can be decoded. + */ +static inline int of_property_read_u8_array(const struct device_node *np, + const char *propname, + u8 *out_values, size_t sz) +{ + int ret = of_property_read_variable_u8_array(np, propname, out_values, + sz, 0); + if (ret >= 0) + return 0; + else + return ret; +} + +/** + * of_property_read_u16_array - Find and read an array of u16 from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_values: pointer to return value, modified only if return value is 0. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 16-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * dts entry of array should be like: + * property = /bits/ 16 <0x5000 0x6000 0x7000>; + * + * The out_values is modified only if a valid u16 value can be decoded. + */ +static inline int of_property_read_u16_array(const struct device_node *np, + const char *propname, + u16 *out_values, size_t sz) +{ + int ret = of_property_read_variable_u16_array(np, propname, out_values, + sz, 0); + if (ret >= 0) + return 0; + else + return ret; +} + +/** + * of_property_read_u32_array - Find and read an array of 32 bit integers + * from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_values: pointer to return value, modified only if return value is 0. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 32-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_values is modified only if a valid u32 value can be decoded. + */ +static inline int of_property_read_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, size_t sz) +{ + int ret = of_property_read_variable_u32_array(np, propname, out_values, + sz, 0); + if (ret >= 0) + return 0; + else + return ret; +} + +/** + * of_property_read_u64_array - Find and read an array of 64 bit integers + * from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_values: pointer to return value, modified only if return value is 0. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 64-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_values is modified only if a valid u64 value can be decoded. + */ +static inline int of_property_read_u64_array(const struct device_node *np, + const char *propname, + u64 *out_values, size_t sz) +{ + int ret = of_property_read_variable_u64_array(np, propname, out_values, + sz, 0); + if (ret >= 0) + return 0; + else + return ret; +} + +/* + * struct property *prop; + * const __be32 *p; + * u32 u; + * + * of_property_for_each_u32(np, "propname", prop, p, u) + * printk("U32 value: %x\n", u); + */ +const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, + u32 *pu); +/* + * struct property *prop; + * const char *s; + * + * of_property_for_each_string(np, "propname", prop, s) + * printk("String value: %s\n", s); + */ +const char *of_prop_next_string(struct property *prop, const char *cur); + +bool of_console_check(struct device_node *dn, char *name, int index); + +extern int of_cpu_node_to_id(struct device_node *np); + +int of_map_rid(struct device_node *np, u32 rid, + const char *map_name, const char *map_mask_name, + struct device_node **target, u32 *id_out); + +#else /* CONFIG_OF */ + +static inline void of_core_init(void) +{ +} + +static inline bool is_of_node(const struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct device_node *to_of_node(const struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline bool of_node_name_eq(const struct device_node *np, const char *name) +{ + return false; +} + +static inline bool of_node_name_prefix(const struct device_node *np, const char *prefix) +{ + return false; +} + +static inline const char* of_node_full_name(const struct device_node *np) +{ + return ""; +} + +static inline struct device_node *of_find_node_by_name(struct device_node *from, + const char *name) +{ + return NULL; +} + +static inline struct device_node *of_find_node_by_type(struct device_node *from, + const char *type) +{ + return NULL; +} + +static inline struct device_node *of_find_matching_node_and_match( + struct device_node *from, + const struct of_device_id *matches, + const struct of_device_id **match) +{ + return NULL; +} + +static inline struct device_node *of_find_node_by_path(const char *path) +{ + return NULL; +} + +static inline struct device_node *of_find_node_opts_by_path(const char *path, + const char **opts) +{ + return NULL; +} + +static inline struct device_node *of_find_node_by_phandle(phandle handle) +{ + return NULL; +} + +static inline struct device_node *of_get_parent(const struct device_node *node) +{ + return NULL; +} + +static inline struct device_node *of_get_next_child( + const struct device_node *node, struct device_node *prev) +{ + return NULL; +} + +static inline struct device_node *of_get_next_available_child( + const struct device_node *node, struct device_node *prev) +{ + return NULL; +} + +static inline struct device_node *of_find_node_with_property( + struct device_node *from, const char *prop_name) +{ + return NULL; +} + +#define of_fwnode_handle(node) NULL + +static inline bool of_have_populated_dt(void) +{ + return false; +} + +static inline struct device_node *of_get_compatible_child(const struct device_node *parent, + const char *compatible) +{ + return NULL; +} + +static inline struct device_node *of_get_child_by_name( + const struct device_node *node, + const char *name) +{ + return NULL; +} + +static inline int of_device_is_compatible(const struct device_node *device, + const char *name) +{ + return 0; +} + +static inline int of_device_compatible_match(struct device_node *device, + const char *const *compat) +{ + return 0; +} + +static inline bool of_device_is_available(const struct device_node *device) +{ + return false; +} + +static inline bool of_device_is_big_endian(const struct device_node *device) +{ + return false; +} + +static inline struct property *of_find_property(const struct device_node *np, + const char *name, + int *lenp) +{ + return NULL; +} + +static inline struct device_node *of_find_compatible_node( + struct device_node *from, + const char *type, + const char *compat) +{ + return NULL; +} + +static inline int of_property_count_elems_of_size(const struct device_node *np, + const char *propname, int elem_size) +{ + return -ENOSYS; +} + +static inline int of_property_read_u8_array(const struct device_node *np, + const char *propname, u8 *out_values, size_t sz) +{ + return -ENOSYS; +} + +static inline int of_property_read_u16_array(const struct device_node *np, + const char *propname, u16 *out_values, size_t sz) +{ + return -ENOSYS; +} + +static inline int of_property_read_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, size_t sz) +{ + return -ENOSYS; +} + +static inline int of_property_read_u64_array(const struct device_node *np, + const char *propname, + u64 *out_values, size_t sz) +{ + return -ENOSYS; +} + +static inline int of_property_read_u32_index(const struct device_node *np, + const char *propname, u32 index, u32 *out_value) +{ + return -ENOSYS; +} + +static inline int of_property_read_u64_index(const struct device_node *np, + const char *propname, u32 index, u64 *out_value) +{ + return -ENOSYS; +} + +static inline const void *of_get_property(const struct device_node *node, + const char *name, + int *lenp) +{ + return NULL; +} + +static inline struct device_node *of_get_cpu_node(int cpu, + unsigned int *thread) +{ + return NULL; +} + +static inline struct device_node *of_get_next_cpu_node(struct device_node *prev) +{ + return NULL; +} + +static inline int of_n_addr_cells(struct device_node *np) +{ + return 0; + +} +static inline int of_n_size_cells(struct device_node *np) +{ + return 0; +} + +static inline int of_property_read_variable_u8_array(const struct device_node *np, + const char *propname, u8 *out_values, + size_t sz_min, size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_variable_u16_array(const struct device_node *np, + const char *propname, u16 *out_values, + size_t sz_min, size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_variable_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, + size_t sz_min, + size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_u64(const struct device_node *np, + const char *propname, u64 *out_value) +{ + return -ENOSYS; +} + +static inline int of_property_read_variable_u64_array(const struct device_node *np, + const char *propname, + u64 *out_values, + size_t sz_min, + size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_string(const struct device_node *np, + const char *propname, + const char **out_string) +{ + return -ENOSYS; +} + +static inline int of_property_match_string(const struct device_node *np, + const char *propname, + const char *string) +{ + return -ENOSYS; +} + +static inline int of_property_read_string_helper(const struct device_node *np, + const char *propname, + const char **out_strs, size_t sz, int index) +{ + return -ENOSYS; +} + +static inline struct device_node *of_parse_phandle(const struct device_node *np, + const char *phandle_name, + int index) +{ + return NULL; +} + +static inline int of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, + const char *cells_name, + int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + +static inline int of_parse_phandle_with_args_map(const struct device_node *np, + const char *list_name, + const char *stem_name, + int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + +static inline int of_parse_phandle_with_fixed_args(const struct device_node *np, + const char *list_name, int cells_count, int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + +static inline int of_count_phandle_with_args(struct device_node *np, + const char *list_name, + const char *cells_name) +{ + return -ENOSYS; +} + +static inline int of_phandle_iterator_init(struct of_phandle_iterator *it, + const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count) +{ + return -ENOSYS; +} + +static inline int of_phandle_iterator_next(struct of_phandle_iterator *it) +{ + return -ENOSYS; +} + +static inline int of_phandle_iterator_args(struct of_phandle_iterator *it, + uint32_t *args, + int size) +{ + return 0; +} + +static inline int of_alias_get_id(struct device_node *np, const char *stem) +{ + return -ENOSYS; +} + +static inline int of_alias_get_highest_id(const char *stem) +{ + return -ENOSYS; +} + +static inline int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits) +{ + return -ENOSYS; +} + +static inline int of_machine_is_compatible(const char *compat) +{ + return 0; +} + +static inline bool of_console_check(const struct device_node *dn, const char *name, int index) +{ + return false; +} + +static inline const __be32 *of_prop_next_u32(struct property *prop, + const __be32 *cur, u32 *pu) +{ + return NULL; +} + +static inline const char *of_prop_next_string(struct property *prop, + const char *cur) +{ + return NULL; +} + +static inline int of_node_check_flag(struct device_node *n, unsigned long flag) +{ + return 0; +} + +static inline int of_node_test_and_set_flag(struct device_node *n, + unsigned long flag) +{ + return 0; +} + +static inline void of_node_set_flag(struct device_node *n, unsigned long flag) +{ +} + +static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) +{ +} + +static inline int of_property_check_flag(struct property *p, unsigned long flag) +{ + return 0; +} + +static inline void of_property_set_flag(struct property *p, unsigned long flag) +{ +} + +static inline void of_property_clear_flag(struct property *p, unsigned long flag) +{ +} + +static inline int of_cpu_node_to_id(struct device_node *np) +{ + return -ENODEV; +} + +static inline int of_map_rid(struct device_node *np, u32 rid, + const char *map_name, const char *map_mask_name, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + +#define of_match_ptr(_ptr) NULL +#define of_match_node(_matches, _node) NULL +#endif /* CONFIG_OF */ + +/* Default string compare functions, Allow arch asm/prom.h to override */ +#if !defined(of_compat_cmp) +#define of_compat_cmp(s1, s2, l) strcasecmp((s1), (s2)) +#define of_prop_cmp(s1, s2) strcmp((s1), (s2)) +#define of_node_cmp(s1, s2) strcasecmp((s1), (s2)) +#endif + +static inline int of_prop_val_eq(struct property *p1, struct property *p2) +{ + return p1->length == p2->length && + !memcmp(p1->value, p2->value, (size_t)p1->length); +} + +#if defined(CONFIG_OF) && defined(CONFIG_NUMA) +extern int of_node_to_nid(struct device_node *np); +#else +static inline int of_node_to_nid(struct device_node *device) +{ + return NUMA_NO_NODE; +} +#endif + +#ifdef CONFIG_OF_NUMA +extern int of_numa_init(void); +#else +static inline int of_numa_init(void) +{ + return -ENOSYS; +} +#endif + +static inline struct device_node *of_find_matching_node( + struct device_node *from, + const struct of_device_id *matches) +{ + return of_find_matching_node_and_match(from, matches, NULL); +} + +static inline const char *of_node_get_device_type(const struct device_node *np) +{ + return of_get_property(np, "device_type", NULL); +} + +static inline bool of_node_is_type(const struct device_node *np, const char *type) +{ + const char *match = of_node_get_device_type(np); + + return np && match && type && !strcmp(match, type); +} + +/** + * of_property_count_u8_elems - Count the number of u8 elements in a property + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device node and count the number of u8 elements + * in it. Returns number of elements on sucess, -EINVAL if the property does + * not exist or its length does not match a multiple of u8 and -ENODATA if the + * property does not have a value. + */ +static inline int of_property_count_u8_elems(const struct device_node *np, + const char *propname) +{ + return of_property_count_elems_of_size(np, propname, sizeof(u8)); +} + +/** + * of_property_count_u16_elems - Count the number of u16 elements in a property + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device node and count the number of u16 elements + * in it. Returns number of elements on sucess, -EINVAL if the property does + * not exist or its length does not match a multiple of u16 and -ENODATA if the + * property does not have a value. + */ +static inline int of_property_count_u16_elems(const struct device_node *np, + const char *propname) +{ + return of_property_count_elems_of_size(np, propname, sizeof(u16)); +} + +/** + * of_property_count_u32_elems - Count the number of u32 elements in a property + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device node and count the number of u32 elements + * in it. Returns number of elements on sucess, -EINVAL if the property does + * not exist or its length does not match a multiple of u32 and -ENODATA if the + * property does not have a value. + */ +static inline int of_property_count_u32_elems(const struct device_node *np, + const char *propname) +{ + return of_property_count_elems_of_size(np, propname, sizeof(u32)); +} + +/** + * of_property_count_u64_elems - Count the number of u64 elements in a property + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device node and count the number of u64 elements + * in it. Returns number of elements on sucess, -EINVAL if the property does + * not exist or its length does not match a multiple of u64 and -ENODATA if the + * property does not have a value. + */ +static inline int of_property_count_u64_elems(const struct device_node *np, + const char *propname) +{ + return of_property_count_elems_of_size(np, propname, sizeof(u64)); +} + +/** + * of_property_read_string_array() - Read an array of strings from a multiple + * strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_strs: output array of string pointers. + * @sz: number of array elements to read. + * + * Search for a property in a device tree node and retrieve a list of + * terminated string values (pointer to data, not a copy) in that property. + * + * If @out_strs is NULL, the number of strings in the property is returned. + */ +static inline int of_property_read_string_array(const struct device_node *np, + const char *propname, const char **out_strs, + size_t sz) +{ + return of_property_read_string_helper(np, propname, out_strs, sz, 0); +} + +/** + * of_property_count_strings() - Find and return the number of strings from a + * multiple strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device tree node and retrieve the number of null + * terminated string contain in it. Returns the number of strings on + * success, -EINVAL if the property does not exist, -ENODATA if property + * does not have a value, and -EILSEQ if the string is not null-terminated + * within the length of the property data. + */ +static inline int of_property_count_strings(const struct device_node *np, + const char *propname) +{ + return of_property_read_string_helper(np, propname, NULL, 0, 0); +} + +/** + * of_property_read_string_index() - Find and read a string from a multiple + * strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the string in the list of strings + * @out_string: pointer to null terminated return string, modified only if + * return value is 0. + * + * Search for a property in a device tree node and retrieve a null + * terminated string value (pointer to data, not a copy) in the list of strings + * contained in that property. + * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if + * property does not have a value, and -EILSEQ if the string is not + * null-terminated within the length of the property data. + * + * The out_string pointer is modified only if a valid string can be decoded. + */ +static inline int of_property_read_string_index(const struct device_node *np, + const char *propname, + int index, const char **output) +{ + int rc = of_property_read_string_helper(np, propname, output, 1, index); + return rc < 0 ? rc : 0; +} + +/** + * of_property_read_bool - Find a property + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device node. + * Returns true if the property exists false otherwise. + */ +static inline bool of_property_read_bool(const struct device_node *np, + const char *propname) +{ + struct property *prop = of_find_property(np, propname, NULL); + + return prop ? true : false; +} + +static inline int of_property_read_u8(const struct device_node *np, + const char *propname, + u8 *out_value) +{ + return of_property_read_u8_array(np, propname, out_value, 1); +} + +static inline int of_property_read_u16(const struct device_node *np, + const char *propname, + u16 *out_value) +{ + return of_property_read_u16_array(np, propname, out_value, 1); +} + +static inline int of_property_read_u32(const struct device_node *np, + const char *propname, + u32 *out_value) +{ + return of_property_read_u32_array(np, propname, out_value, 1); +} + +static inline int of_property_read_s32(const struct device_node *np, + const char *propname, + s32 *out_value) +{ + return of_property_read_u32(np, propname, (u32*) out_value); +} + +#define of_for_each_phandle(it, err, np, ln, cn, cc) \ + for (of_phandle_iterator_init((it), (np), (ln), (cn), (cc)), \ + err = of_phandle_iterator_next(it); \ + err == 0; \ + err = of_phandle_iterator_next(it)) + +#define of_property_for_each_u32(np, propname, prop, p, u) \ + for (prop = of_find_property(np, propname, NULL), \ + p = of_prop_next_u32(prop, NULL, &u); \ + p; \ + p = of_prop_next_u32(prop, p, &u)) + +#define of_property_for_each_string(np, propname, prop, s) \ + for (prop = of_find_property(np, propname, NULL), \ + s = of_prop_next_string(prop, NULL); \ + s; \ + s = of_prop_next_string(prop, s)) + +#define for_each_node_by_name(dn, name) \ + for (dn = of_find_node_by_name(NULL, name); dn; \ + dn = of_find_node_by_name(dn, name)) +#define for_each_node_by_type(dn, type) \ + for (dn = of_find_node_by_type(NULL, type); dn; \ + dn = of_find_node_by_type(dn, type)) +#define for_each_compatible_node(dn, type, compatible) \ + for (dn = of_find_compatible_node(NULL, type, compatible); dn; \ + dn = of_find_compatible_node(dn, type, compatible)) +#define for_each_matching_node(dn, matches) \ + for (dn = of_find_matching_node(NULL, matches); dn; \ + dn = of_find_matching_node(dn, matches)) +#define for_each_matching_node_and_match(dn, matches, match) \ + for (dn = of_find_matching_node_and_match(NULL, matches, match); \ + dn; dn = of_find_matching_node_and_match(dn, matches, match)) + +#define for_each_child_of_node(parent, child) \ + for (child = of_get_next_child(parent, NULL); child != NULL; \ + child = of_get_next_child(parent, child)) +#define for_each_available_child_of_node(parent, child) \ + for (child = of_get_next_available_child(parent, NULL); child != NULL; \ + child = of_get_next_available_child(parent, child)) + +#define for_each_of_cpu_node(cpu) \ + for (cpu = of_get_next_cpu_node(NULL); cpu != NULL; \ + cpu = of_get_next_cpu_node(cpu)) + +#define for_each_node_with_property(dn, prop_name) \ + for (dn = of_find_node_with_property(NULL, prop_name); dn; \ + dn = of_find_node_with_property(dn, prop_name)) + +static inline int of_get_child_count(const struct device_node *np) +{ + struct device_node *child; + int num = 0; + + for_each_child_of_node(np, child) + num++; + + return num; +} + +static inline int of_get_available_child_count(const struct device_node *np) +{ + struct device_node *child; + int num = 0; + + for_each_available_child_of_node(np, child) + num++; + + return num; +} + +#if defined(CONFIG_OF) && !defined(MODULE) +#define _OF_DECLARE(table, name, compat, fn, fn_type) \ + static const struct of_device_id __of_table_##name \ + __used __section(__##table##_of_table) \ + = { .compatible = compat, \ + .data = (fn == (fn_type)NULL) ? fn : fn } +#else +#define _OF_DECLARE(table, name, compat, fn, fn_type) \ + static const struct of_device_id __of_table_##name \ + __attribute__((unused)) \ + = { .compatible = compat, \ + .data = (fn == (fn_type)NULL) ? fn : fn } +#endif + +typedef int (*of_init_fn_2)(struct device_node *, struct device_node *); +typedef int (*of_init_fn_1_ret)(struct device_node *); +typedef void (*of_init_fn_1)(struct device_node *); + +#define OF_DECLARE_1(table, name, compat, fn) \ + _OF_DECLARE(table, name, compat, fn, of_init_fn_1) +#define OF_DECLARE_1_RET(table, name, compat, fn) \ + _OF_DECLARE(table, name, compat, fn, of_init_fn_1_ret) +#define OF_DECLARE_2(table, name, compat, fn) \ + _OF_DECLARE(table, name, compat, fn, of_init_fn_2) + +/** + * struct of_changeset_entry - Holds a changeset entry + * + * @node: list_head for the log list + * @action: notifier action + * @np: pointer to the device node affected + * @prop: pointer to the property affected + * @old_prop: hold a pointer to the original property + * + * Every modification of the device tree during a changeset + * is held in a list of of_changeset_entry structures. + * That way we can recover from a partial application, or we can + * revert the changeset + */ +struct of_changeset_entry { + struct list_head node; + unsigned long action; + struct device_node *np; + struct property *prop; + struct property *old_prop; +}; + +/** + * struct of_changeset - changeset tracker structure + * + * @entries: list_head for the changeset entries + * + * changesets are a convenient way to apply bulk changes to the + * live tree. In case of an error, changes are rolled-back. + * changesets live on after initial application, and if not + * destroyed after use, they can be reverted in one single call. + */ +struct of_changeset { + struct list_head entries; +}; + +enum of_reconfig_change { + OF_RECONFIG_NO_CHANGE = 0, + OF_RECONFIG_CHANGE_ADD, + OF_RECONFIG_CHANGE_REMOVE, +}; + +#ifdef CONFIG_OF_DYNAMIC +extern int of_reconfig_notifier_register(struct notifier_block *); +extern int of_reconfig_notifier_unregister(struct notifier_block *); +extern int of_reconfig_notify(unsigned long, struct of_reconfig_data *rd); +extern int of_reconfig_get_state_change(unsigned long action, + struct of_reconfig_data *arg); + +extern void of_changeset_init(struct of_changeset *ocs); +extern void of_changeset_destroy(struct of_changeset *ocs); +extern int of_changeset_apply(struct of_changeset *ocs); +extern int of_changeset_revert(struct of_changeset *ocs); +extern int of_changeset_action(struct of_changeset *ocs, + unsigned long action, struct device_node *np, + struct property *prop); + +static inline int of_changeset_attach_node(struct of_changeset *ocs, + struct device_node *np) +{ + return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL); +} + +static inline int of_changeset_detach_node(struct of_changeset *ocs, + struct device_node *np) +{ + return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL); +} + +static inline int of_changeset_add_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop); +} + +static inline int of_changeset_remove_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop); +} + +static inline int of_changeset_update_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop); +} +#else /* CONFIG_OF_DYNAMIC */ +static inline int of_reconfig_notifier_register(struct notifier_block *nb) +{ + return -EINVAL; +} +static inline int of_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return -EINVAL; +} +static inline int of_reconfig_notify(unsigned long action, + struct of_reconfig_data *arg) +{ + return -EINVAL; +} +static inline int of_reconfig_get_state_change(unsigned long action, + struct of_reconfig_data *arg) +{ + return -EINVAL; +} +#endif /* CONFIG_OF_DYNAMIC */ + +/** + * of_device_is_system_power_controller - Tells if system-power-controller is found for device_node + * @np: Pointer to the given device_node + * + * return true if present false otherwise + */ +static inline bool of_device_is_system_power_controller(const struct device_node *np) +{ + return of_property_read_bool(np, "system-power-controller"); +} + +/** + * Overlay support + */ + +enum of_overlay_notify_action { + OF_OVERLAY_PRE_APPLY = 0, + OF_OVERLAY_POST_APPLY, + OF_OVERLAY_PRE_REMOVE, + OF_OVERLAY_POST_REMOVE, +}; + +struct of_overlay_notify_data { + struct device_node *overlay; + struct device_node *target; +}; + +#ifdef CONFIG_OF_OVERLAY + +int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, + int *ovcs_id); +int of_overlay_remove(int *ovcs_id); +int of_overlay_remove_all(void); + +int of_overlay_notifier_register(struct notifier_block *nb); +int of_overlay_notifier_unregister(struct notifier_block *nb); + +#else + +static inline int of_overlay_fdt_apply(void *overlay_fdt, u32 overlay_fdt_size, + int *ovcs_id) +{ + return -ENOTSUPP; +} + +static inline int of_overlay_remove(int *ovcs_id) +{ + return -ENOTSUPP; +} + +static inline int of_overlay_remove_all(void) +{ + return -ENOTSUPP; +} + +static inline int of_overlay_notifier_register(struct notifier_block *nb) +{ + return 0; +} + +static inline int of_overlay_notifier_unregister(struct notifier_block *nb) +{ + return 0; +} + +#endif + +#endif /* _LINUX_OF_H */ diff --git a/t/tree/include/linux/of_platform.h b/t/tree/include/linux/of_platform.h new file mode 100644 index 00000000..84a96662 --- /dev/null +++ b/t/tree/include/linux/of_platform.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef _LINUX_OF_PLATFORM_H +#define _LINUX_OF_PLATFORM_H +/* + * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. + * + */ + +#include +#include +#include +#include +#include + +/** + * struct of_dev_auxdata - lookup table entry for device names & platform_data + * @compatible: compatible value of node to match against node + * @phys_addr: Start address of registers to match against node + * @name: Name to assign for matching nodes + * @platform_data: platform_data to assign for matching nodes + * + * This lookup table allows the caller of of_platform_populate() to override + * the names of devices when creating devices from the device tree. The table + * should be terminated with an empty entry. It also allows the platform_data + * pointer to be set. + * + * The reason for this functionality is that some Linux infrastructure uses + * the device name to look up a specific device, but the Linux-specific names + * are not encoded into the device tree, so the kernel needs to provide specific + * values. + * + * Note: Using an auxdata lookup table should be considered a last resort when + * converting a platform to use the DT. Normally the automatically generated + * device name will not matter, and drivers should obtain data from the device + * node instead of from an anonymous platform_data pointer. + */ +struct of_dev_auxdata { + char *compatible; + resource_size_t phys_addr; + char *name; + void *platform_data; +}; + +/* Macro to simplify populating a lookup table */ +#define OF_DEV_AUXDATA(_compat,_phys,_name,_pdata) \ + { .compatible = _compat, .phys_addr = _phys, .name = _name, \ + .platform_data = _pdata } + +extern const struct of_device_id of_default_bus_match_table[]; + +/* Platform drivers register/unregister */ +extern struct platform_device *of_device_alloc(struct device_node *np, + const char *bus_id, + struct device *parent); +#ifdef CONFIG_OF +extern struct platform_device *of_find_device_by_node(struct device_node *np); +#else +static inline struct platform_device *of_find_device_by_node(struct device_node *np) +{ + return NULL; +} +#endif + +/* Platform devices and busses creation */ +extern struct platform_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent); + +extern int of_platform_device_destroy(struct device *dev, void *data); +extern int of_platform_bus_probe(struct device_node *root, + const struct of_device_id *matches, + struct device *parent); +#ifdef CONFIG_OF_ADDRESS +extern int of_platform_populate(struct device_node *root, + const struct of_device_id *matches, + const struct of_dev_auxdata *lookup, + struct device *parent); +extern int of_platform_default_populate(struct device_node *root, + const struct of_dev_auxdata *lookup, + struct device *parent); +extern void of_platform_depopulate(struct device *parent); + +extern int devm_of_platform_populate(struct device *dev); + +extern void devm_of_platform_depopulate(struct device *dev); +#else +static inline int of_platform_populate(struct device_node *root, + const struct of_device_id *matches, + const struct of_dev_auxdata *lookup, + struct device *parent) +{ + return -ENODEV; +} +static inline int of_platform_default_populate(struct device_node *root, + const struct of_dev_auxdata *lookup, + struct device *parent) +{ + return -ENODEV; +} +static inline void of_platform_depopulate(struct device *parent) { } + +static inline int devm_of_platform_populate(struct device *dev) +{ + return -ENODEV; +} + +static inline void devm_of_platform_depopulate(struct device *dev) { } +#endif + +#if defined(CONFIG_OF_DYNAMIC) && defined(CONFIG_OF_ADDRESS) +extern void of_platform_register_reconfig_notifier(void); +#else +static inline void of_platform_register_reconfig_notifier(void) { } +#endif + +#endif /* _LINUX_OF_PLATFORM_H */ diff --git a/t/tree/include/linux/plist.h b/t/tree/include/linux/plist.h new file mode 100644 index 00000000..66bab1bc --- /dev/null +++ b/t/tree/include/linux/plist.h @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Descending-priority-sorted double-linked list + * + * (C) 2002-2003 Intel Corp + * Inaky Perez-Gonzalez . + * + * 2001-2005 (c) MontaVista Software, Inc. + * Daniel Walker + * + * (C) 2005 Thomas Gleixner + * + * Simplifications of the original code by + * Oleg Nesterov + * + * Based on simple lists (include/linux/list.h). + * + * This is a priority-sorted list of nodes; each node has a + * priority from INT_MIN (highest) to INT_MAX (lowest). + * + * Addition is O(K), removal is O(1), change of priority of a node is + * O(K) and K is the number of RT priority levels used in the system. + * (1 <= K <= 99) + * + * This list is really a list of lists: + * + * - The tier 1 list is the prio_list, different priority nodes. + * + * - The tier 2 list is the node_list, serialized nodes. + * + * Simple ASCII art explanation: + * + * pl:prio_list (only for plist_node) + * nl:node_list + * HEAD| NODE(S) + * | + * ||------------------------------------| + * ||->|pl|<->|pl|<--------------->|pl|<-| + * | |10| |21| |21| |21| |40| (prio) + * | | | | | | | | | | | + * | | | | | | | | | | | + * |->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-| + * |-------------------------------------------| + * + * The nodes on the prio_list list are sorted by priority to simplify + * the insertion of new nodes. There are no nodes with duplicate + * priorites on the list. + * + * The nodes on the node_list are ordered by priority and can contain + * entries which have the same priority. Those entries are ordered + * FIFO + * + * Addition means: look for the prio_list node in the prio_list + * for the priority of the node and insert it before the node_list + * entry of the next prio_list node. If it is the first node of + * that priority, add it to the prio_list in the right position and + * insert it into the serialized node_list list + * + * Removal means remove it from the node_list and remove it from + * the prio_list if the node_list list_head is non empty. In case + * of removal from the prio_list it must be checked whether other + * entries of the same priority are on the list or not. If there + * is another entry of the same priority then this entry has to + * replace the removed entry on the prio_list. If the entry which + * is removed is the only entry of this priority then a simple + * remove from both list is sufficient. + * + * INT_MIN is the highest priority, 0 is the medium highest, INT_MAX + * is lowest priority. + * + * No locking is done, up to the caller. + */ +#ifndef _LINUX_PLIST_H_ +#define _LINUX_PLIST_H_ + +#include +#include + +struct plist_head { + struct list_head node_list; +}; + +struct plist_node { + int prio; + struct list_head prio_list; + struct list_head node_list; +}; + +/** + * PLIST_HEAD_INIT - static struct plist_head initializer + * @head: struct plist_head variable name + */ +#define PLIST_HEAD_INIT(head) \ +{ \ + .node_list = LIST_HEAD_INIT((head).node_list) \ +} + +/** + * PLIST_HEAD - declare and init plist_head + * @head: name for struct plist_head variable + */ +#define PLIST_HEAD(head) \ + struct plist_head head = PLIST_HEAD_INIT(head) + +/** + * PLIST_NODE_INIT - static struct plist_node initializer + * @node: struct plist_node variable name + * @__prio: initial node priority + */ +#define PLIST_NODE_INIT(node, __prio) \ +{ \ + .prio = (__prio), \ + .prio_list = LIST_HEAD_INIT((node).prio_list), \ + .node_list = LIST_HEAD_INIT((node).node_list), \ +} + +/** + * plist_head_init - dynamic struct plist_head initializer + * @head: &struct plist_head pointer + */ +static inline void +plist_head_init(struct plist_head *head) +{ + INIT_LIST_HEAD(&head->node_list); +} + +/** + * plist_node_init - Dynamic struct plist_node initializer + * @node: &struct plist_node pointer + * @prio: initial node priority + */ +static inline void plist_node_init(struct plist_node *node, int prio) +{ + node->prio = prio; + INIT_LIST_HEAD(&node->prio_list); + INIT_LIST_HEAD(&node->node_list); +} + +extern void plist_add(struct plist_node *node, struct plist_head *head); +extern void plist_del(struct plist_node *node, struct plist_head *head); + +extern void plist_requeue(struct plist_node *node, struct plist_head *head); + +/** + * plist_for_each - iterate over the plist + * @pos: the type * to use as a loop counter + * @head: the head for your list + */ +#define plist_for_each(pos, head) \ + list_for_each_entry(pos, &(head)->node_list, node_list) + +/** + * plist_for_each_continue - continue iteration over the plist + * @pos: the type * to use as a loop cursor + * @head: the head for your list + * + * Continue to iterate over plist, continuing after the current position. + */ +#define plist_for_each_continue(pos, head) \ + list_for_each_entry_continue(pos, &(head)->node_list, node_list) + +/** + * plist_for_each_safe - iterate safely over a plist of given type + * @pos: the type * to use as a loop counter + * @n: another type * to use as temporary storage + * @head: the head for your list + * + * Iterate over a plist of given type, safe against removal of list entry. + */ +#define plist_for_each_safe(pos, n, head) \ + list_for_each_entry_safe(pos, n, &(head)->node_list, node_list) + +/** + * plist_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter + * @head: the head for your list + * @mem: the name of the list_head within the struct + */ +#define plist_for_each_entry(pos, head, mem) \ + list_for_each_entry(pos, &(head)->node_list, mem.node_list) + +/** + * plist_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor + * @head: the head for your list + * @m: the name of the list_head within the struct + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define plist_for_each_entry_continue(pos, head, m) \ + list_for_each_entry_continue(pos, &(head)->node_list, m.node_list) + +/** + * plist_for_each_entry_safe - iterate safely over list of given type + * @pos: the type * to use as a loop counter + * @n: another type * to use as temporary storage + * @head: the head for your list + * @m: the name of the list_head within the struct + * + * Iterate over list of given type, safe against removal of list entry. + */ +#define plist_for_each_entry_safe(pos, n, head, m) \ + list_for_each_entry_safe(pos, n, &(head)->node_list, m.node_list) + +/** + * plist_head_empty - return !0 if a plist_head is empty + * @head: &struct plist_head pointer + */ +static inline int plist_head_empty(const struct plist_head *head) +{ + return list_empty(&head->node_list); +} + +/** + * plist_node_empty - return !0 if plist_node is not on a list + * @node: &struct plist_node pointer + */ +static inline int plist_node_empty(const struct plist_node *node) +{ + return list_empty(&node->node_list); +} + +/* All functions below assume the plist_head is not empty. */ + +/** + * plist_first_entry - get the struct for the first entry + * @head: the &struct plist_head pointer + * @type: the type of the struct this is embedded in + * @member: the name of the list_head within the struct + */ +#ifdef CONFIG_DEBUG_PLIST +# define plist_first_entry(head, type, member) \ +({ \ + WARN_ON(plist_head_empty(head)); \ + container_of(plist_first(head), type, member); \ +}) +#else +# define plist_first_entry(head, type, member) \ + container_of(plist_first(head), type, member) +#endif + +/** + * plist_last_entry - get the struct for the last entry + * @head: the &struct plist_head pointer + * @type: the type of the struct this is embedded in + * @member: the name of the list_head within the struct + */ +#ifdef CONFIG_DEBUG_PLIST +# define plist_last_entry(head, type, member) \ +({ \ + WARN_ON(plist_head_empty(head)); \ + container_of(plist_last(head), type, member); \ +}) +#else +# define plist_last_entry(head, type, member) \ + container_of(plist_last(head), type, member) +#endif + +/** + * plist_next - get the next entry in list + * @pos: the type * to cursor + */ +#define plist_next(pos) \ + list_next_entry(pos, node_list) + +/** + * plist_prev - get the prev entry in list + * @pos: the type * to cursor + */ +#define plist_prev(pos) \ + list_prev_entry(pos, node_list) + +/** + * plist_first - return the first node (and thus, highest priority) + * @head: the &struct plist_head pointer + * + * Assumes the plist is _not_ empty. + */ +static inline struct plist_node *plist_first(const struct plist_head *head) +{ + return list_entry(head->node_list.next, + struct plist_node, node_list); +} + +/** + * plist_last - return the last node (and thus, lowest priority) + * @head: the &struct plist_head pointer + * + * Assumes the plist is _not_ empty. + */ +static inline struct plist_node *plist_last(const struct plist_head *head) +{ + return list_entry(head->node_list.prev, + struct plist_node, node_list); +} + +#endif diff --git a/t/tree/include/linux/pm.h b/t/tree/include/linux/pm.h new file mode 100644 index 00000000..4c441be0 --- /dev/null +++ b/t/tree/include/linux/pm.h @@ -0,0 +1,813 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * pm.h - Power management interface + * + * Copyright (C) 2000 Andrew Henroid + */ + +#ifndef _LINUX_PM_H +#define _LINUX_PM_H + +#include +#include +#include +#include +#include +#include +#include + +/* + * Callbacks for platform drivers to implement. + */ +extern void (*pm_power_off)(void); +extern void (*pm_power_off_prepare)(void); + +struct device; /* we have a circular dep with device.h */ +#ifdef CONFIG_VT_CONSOLE_SLEEP +extern void pm_vt_switch_required(struct device *dev, bool required); +extern void pm_vt_switch_unregister(struct device *dev); +#else +static inline void pm_vt_switch_required(struct device *dev, bool required) +{ +} +static inline void pm_vt_switch_unregister(struct device *dev) +{ +} +#endif /* CONFIG_VT_CONSOLE_SLEEP */ + +/* + * Device power management + */ + +struct device; + +#ifdef CONFIG_PM +extern const char power_group_name[]; /* = "power" */ +#else +#define power_group_name NULL +#endif + +typedef struct pm_message { + int event; +} pm_message_t; + +/** + * struct dev_pm_ops - device PM callbacks. + * + * @prepare: The principal role of this callback is to prevent new children of + * the device from being registered after it has returned (the driver's + * subsystem and generally the rest of the kernel is supposed to prevent + * new calls to the probe method from being made too once @prepare() has + * succeeded). If @prepare() detects a situation it cannot handle (e.g. + * registration of a child already in progress), it may return -EAGAIN, so + * that the PM core can execute it once again (e.g. after a new child has + * been registered) to recover from the race condition. + * This method is executed for all kinds of suspend transitions and is + * followed by one of the suspend callbacks: @suspend(), @freeze(), or + * @poweroff(). If the transition is a suspend to memory or standby (that + * is, not related to hibernation), the return value of @prepare() may be + * used to indicate to the PM core to leave the device in runtime suspend + * if applicable. Namely, if @prepare() returns a positive number, the PM + * core will understand that as a declaration that the device appears to be + * runtime-suspended and it may be left in that state during the entire + * transition and during the subsequent resume if all of its descendants + * are left in runtime suspend too. If that happens, @complete() will be + * executed directly after @prepare() and it must ensure the proper + * functioning of the device after the system resume. + * The PM core executes subsystem-level @prepare() for all devices before + * starting to invoke suspend callbacks for any of them, so generally + * devices may be assumed to be functional or to respond to runtime resume + * requests while @prepare() is being executed. However, device drivers + * may NOT assume anything about the availability of user space at that + * time and it is NOT valid to request firmware from within @prepare() + * (it's too late to do that). It also is NOT valid to allocate + * substantial amounts of memory from @prepare() in the GFP_KERNEL mode. + * [To work around these limitations, drivers may register suspend and + * hibernation notifiers to be executed before the freezing of tasks.] + * + * @complete: Undo the changes made by @prepare(). This method is executed for + * all kinds of resume transitions, following one of the resume callbacks: + * @resume(), @thaw(), @restore(). Also called if the state transition + * fails before the driver's suspend callback: @suspend(), @freeze() or + * @poweroff(), can be executed (e.g. if the suspend callback fails for one + * of the other devices that the PM core has unsuccessfully attempted to + * suspend earlier). + * The PM core executes subsystem-level @complete() after it has executed + * the appropriate resume callbacks for all devices. If the corresponding + * @prepare() at the beginning of the suspend transition returned a + * positive number and the device was left in runtime suspend (without + * executing any suspend and resume callbacks for it), @complete() will be + * the only callback executed for the device during resume. In that case, + * @complete() must be prepared to do whatever is necessary to ensure the + * proper functioning of the device after the system resume. To this end, + * @complete() can check the power.direct_complete flag of the device to + * learn whether (unset) or not (set) the previous suspend and resume + * callbacks have been executed for it. + * + * @suspend: Executed before putting the system into a sleep state in which the + * contents of main memory are preserved. The exact action to perform + * depends on the device's subsystem (PM domain, device type, class or bus + * type), but generally the device must be quiescent after subsystem-level + * @suspend() has returned, so that it doesn't do any I/O or DMA. + * Subsystem-level @suspend() is executed for all devices after invoking + * subsystem-level @prepare() for all of them. + * + * @suspend_late: Continue operations started by @suspend(). For a number of + * devices @suspend_late() may point to the same callback routine as the + * runtime suspend callback. + * + * @resume: Executed after waking the system up from a sleep state in which the + * contents of main memory were preserved. The exact action to perform + * depends on the device's subsystem, but generally the driver is expected + * to start working again, responding to hardware events and software + * requests (the device itself may be left in a low-power state, waiting + * for a runtime resume to occur). The state of the device at the time its + * driver's @resume() callback is run depends on the platform and subsystem + * the device belongs to. On most platforms, there are no restrictions on + * availability of resources like clocks during @resume(). + * Subsystem-level @resume() is executed for all devices after invoking + * subsystem-level @resume_noirq() for all of them. + * + * @resume_early: Prepare to execute @resume(). For a number of devices + * @resume_early() may point to the same callback routine as the runtime + * resume callback. + * + * @freeze: Hibernation-specific, executed before creating a hibernation image. + * Analogous to @suspend(), but it should not enable the device to signal + * wakeup events or change its power state. The majority of subsystems + * (with the notable exception of the PCI bus type) expect the driver-level + * @freeze() to save the device settings in memory to be used by @restore() + * during the subsequent resume from hibernation. + * Subsystem-level @freeze() is executed for all devices after invoking + * subsystem-level @prepare() for all of them. + * + * @freeze_late: Continue operations started by @freeze(). Analogous to + * @suspend_late(), but it should not enable the device to signal wakeup + * events or change its power state. + * + * @thaw: Hibernation-specific, executed after creating a hibernation image OR + * if the creation of an image has failed. Also executed after a failing + * attempt to restore the contents of main memory from such an image. + * Undo the changes made by the preceding @freeze(), so the device can be + * operated in the same way as immediately before the call to @freeze(). + * Subsystem-level @thaw() is executed for all devices after invoking + * subsystem-level @thaw_noirq() for all of them. It also may be executed + * directly after @freeze() in case of a transition error. + * + * @thaw_early: Prepare to execute @thaw(). Undo the changes made by the + * preceding @freeze_late(). + * + * @poweroff: Hibernation-specific, executed after saving a hibernation image. + * Analogous to @suspend(), but it need not save the device's settings in + * memory. + * Subsystem-level @poweroff() is executed for all devices after invoking + * subsystem-level @prepare() for all of them. + * + * @poweroff_late: Continue operations started by @poweroff(). Analogous to + * @suspend_late(), but it need not save the device's settings in memory. + * + * @restore: Hibernation-specific, executed after restoring the contents of main + * memory from a hibernation image, analogous to @resume(). + * + * @restore_early: Prepare to execute @restore(), analogous to @resume_early(). + * + * @suspend_noirq: Complete the actions started by @suspend(). Carry out any + * additional operations required for suspending the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while @suspend_noirq() is being executed. + * It generally is expected that the device will be in a low-power state + * (appropriate for the target system sleep state) after subsystem-level + * @suspend_noirq() has returned successfully. If the device can generate + * system wakeup signals and is enabled to wake up the system, it should be + * configured to do so at that time. However, depending on the platform + * and device's subsystem, @suspend() or @suspend_late() may be allowed to + * put the device into the low-power state and configure it to generate + * wakeup signals, in which case it generally is not necessary to define + * @suspend_noirq(). + * + * @resume_noirq: Prepare for the execution of @resume() by carrying out any + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * @resume_noirq() is being executed. + * + * @freeze_noirq: Complete the actions started by @freeze(). Carry out any + * additional operations required for freezing the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while @freeze_noirq() is being executed. + * The power state of the device should not be changed by either @freeze(), + * or @freeze_late(), or @freeze_noirq() and it should not be configured to + * signal system wakeup by any of these callbacks. + * + * @thaw_noirq: Prepare for the execution of @thaw() by carrying out any + * operations required for thawing the device that might be racing with its + * driver's interrupt handler, which is guaranteed not to run while + * @thaw_noirq() is being executed. + * + * @poweroff_noirq: Complete the actions started by @poweroff(). Analogous to + * @suspend_noirq(), but it need not save the device's settings in memory. + * + * @restore_noirq: Prepare for the execution of @restore() by carrying out any + * operations required for thawing the device that might be racing with its + * driver's interrupt handler, which is guaranteed not to run while + * @restore_noirq() is being executed. Analogous to @resume_noirq(). + * + * @runtime_suspend: Prepare the device for a condition in which it won't be + * able to communicate with the CPU(s) and RAM due to power management. + * This need not mean that the device should be put into a low-power state. + * For example, if the device is behind a link which is about to be turned + * off, the device may remain at full power. If the device does go to low + * power and is capable of generating runtime wakeup events, remote wakeup + * (i.e., a hardware mechanism allowing the device to request a change of + * its power state via an interrupt) should be enabled for it. + * + * @runtime_resume: Put the device into the fully active state in response to a + * wakeup event generated by hardware or at the request of software. If + * necessary, put the device into the full-power state and restore its + * registers, so that it is fully operational. + * + * @runtime_idle: Device appears to be inactive and it might be put into a + * low-power state if all of the necessary conditions are satisfied. + * Check these conditions, and return 0 if it's appropriate to let the PM + * core queue a suspend request for the device. + * + * Several device power state transitions are externally visible, affecting + * the state of pending I/O queues and (for drivers that touch hardware) + * interrupts, wakeups, DMA, and other hardware state. There may also be + * internal transitions to various low-power modes which are transparent + * to the rest of the driver stack (such as a driver that's ON gating off + * clocks which are not in active use). + * + * The externally visible transitions are handled with the help of callbacks + * included in this structure in such a way that, typically, two levels of + * callbacks are involved. First, the PM core executes callbacks provided by PM + * domains, device types, classes and bus types. They are the subsystem-level + * callbacks expected to execute callbacks provided by device drivers, although + * they may choose not to do that. If the driver callbacks are executed, they + * have to collaborate with the subsystem-level callbacks to achieve the goals + * appropriate for the given system transition, given transition phase and the + * subsystem the device belongs to. + * + * All of the above callbacks, except for @complete(), return error codes. + * However, the error codes returned by @resume(), @thaw(), @restore(), + * @resume_noirq(), @thaw_noirq(), and @restore_noirq(), do not cause the PM + * core to abort the resume transition during which they are returned. The + * error codes returned in those cases are only printed to the system logs for + * debugging purposes. Still, it is recommended that drivers only return error + * codes from their resume methods in case of an unrecoverable failure (i.e. + * when the device being handled refuses to resume and becomes unusable) to + * allow the PM core to be modified in the future, so that it can avoid + * attempting to handle devices that failed to resume and their children. + * + * It is allowed to unregister devices while the above callbacks are being + * executed. However, a callback routine MUST NOT try to unregister the device + * it was called for, although it may unregister children of that device (for + * example, if it detects that a child was unplugged while the system was + * asleep). + * + * There also are callbacks related to runtime power management of devices. + * Again, as a rule these callbacks are executed by the PM core for subsystems + * (PM domains, device types, classes and bus types) and the subsystem-level + * callbacks are expected to invoke the driver callbacks. Moreover, the exact + * actions to be performed by a device driver's callbacks generally depend on + * the platform and subsystem the device belongs to. + * + * Refer to Documentation/power/runtime_pm.rst for more information about the + * role of the @runtime_suspend(), @runtime_resume() and @runtime_idle() + * callbacks in device runtime power management. + */ +struct dev_pm_ops { + int (*prepare)(struct device *dev); + void (*complete)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + int (*freeze)(struct device *dev); + int (*thaw)(struct device *dev); + int (*poweroff)(struct device *dev); + int (*restore)(struct device *dev); + int (*suspend_late)(struct device *dev); + int (*resume_early)(struct device *dev); + int (*freeze_late)(struct device *dev); + int (*thaw_early)(struct device *dev); + int (*poweroff_late)(struct device *dev); + int (*restore_early)(struct device *dev); + int (*suspend_noirq)(struct device *dev); + int (*resume_noirq)(struct device *dev); + int (*freeze_noirq)(struct device *dev); + int (*thaw_noirq)(struct device *dev); + int (*poweroff_noirq)(struct device *dev); + int (*restore_noirq)(struct device *dev); + int (*runtime_suspend)(struct device *dev); + int (*runtime_resume)(struct device *dev); + int (*runtime_idle)(struct device *dev); +}; + +#ifdef CONFIG_PM_SLEEP +#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend = suspend_fn, \ + .resume = resume_fn, \ + .freeze = suspend_fn, \ + .thaw = resume_fn, \ + .poweroff = suspend_fn, \ + .restore = resume_fn, +#else +#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) +#endif + +#ifdef CONFIG_PM_SLEEP +#define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend_late = suspend_fn, \ + .resume_early = resume_fn, \ + .freeze_late = suspend_fn, \ + .thaw_early = resume_fn, \ + .poweroff_late = suspend_fn, \ + .restore_early = resume_fn, +#else +#define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) +#endif + +#ifdef CONFIG_PM_SLEEP +#define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend_noirq = suspend_fn, \ + .resume_noirq = resume_fn, \ + .freeze_noirq = suspend_fn, \ + .thaw_noirq = resume_fn, \ + .poweroff_noirq = suspend_fn, \ + .restore_noirq = resume_fn, +#else +#define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) +#endif + +#ifdef CONFIG_PM +#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ + .runtime_suspend = suspend_fn, \ + .runtime_resume = resume_fn, \ + .runtime_idle = idle_fn, +#else +#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) +#endif + +/* + * Use this if you want to use the same suspend and resume callbacks for suspend + * to RAM and hibernation. + */ +#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ +const struct dev_pm_ops name = { \ + SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ +} + +/* + * Use this for defining a set of PM operations to be used in all situations + * (system suspend, hibernation or runtime PM). + * NOTE: In general, system suspend callbacks, .suspend() and .resume(), should + * be different from the corresponding runtime PM callbacks, .runtime_suspend(), + * and .runtime_resume(), because .runtime_suspend() always works on an already + * quiescent device, while .suspend() should assume that the device may be doing + * something when it is called (it should ensure that the device will be + * quiescent after it has returned). Therefore it's better to point the "late" + * suspend and "early" resume callback pointers, .suspend_late() and + * .resume_early(), to the same routines as .runtime_suspend() and + * .runtime_resume(), respectively (and analogously for hibernation). + */ +#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \ +const struct dev_pm_ops name = { \ + SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ +} + +/* + * PM_EVENT_ messages + * + * The following PM_EVENT_ messages are defined for the internal use of the PM + * core, in order to provide a mechanism allowing the high level suspend and + * hibernation code to convey the necessary information to the device PM core + * code: + * + * ON No transition. + * + * FREEZE System is going to hibernate, call ->prepare() and ->freeze() + * for all devices. + * + * SUSPEND System is going to suspend, call ->prepare() and ->suspend() + * for all devices. + * + * HIBERNATE Hibernation image has been saved, call ->prepare() and + * ->poweroff() for all devices. + * + * QUIESCE Contents of main memory are going to be restored from a (loaded) + * hibernation image, call ->prepare() and ->freeze() for all + * devices. + * + * RESUME System is resuming, call ->resume() and ->complete() for all + * devices. + * + * THAW Hibernation image has been created, call ->thaw() and + * ->complete() for all devices. + * + * RESTORE Contents of main memory have been restored from a hibernation + * image, call ->restore() and ->complete() for all devices. + * + * RECOVER Creation of a hibernation image or restoration of the main + * memory contents from a hibernation image has failed, call + * ->thaw() and ->complete() for all devices. + * + * The following PM_EVENT_ messages are defined for internal use by + * kernel subsystems. They are never issued by the PM core. + * + * USER_SUSPEND Manual selective suspend was issued by userspace. + * + * USER_RESUME Manual selective resume was issued by userspace. + * + * REMOTE_WAKEUP Remote-wakeup request was received from the device. + * + * AUTO_SUSPEND Automatic (device idle) runtime suspend was + * initiated by the subsystem. + * + * AUTO_RESUME Automatic (device needed) runtime resume was + * requested by a driver. + */ + +#define PM_EVENT_INVALID (-1) +#define PM_EVENT_ON 0x0000 +#define PM_EVENT_FREEZE 0x0001 +#define PM_EVENT_SUSPEND 0x0002 +#define PM_EVENT_HIBERNATE 0x0004 +#define PM_EVENT_QUIESCE 0x0008 +#define PM_EVENT_RESUME 0x0010 +#define PM_EVENT_THAW 0x0020 +#define PM_EVENT_RESTORE 0x0040 +#define PM_EVENT_RECOVER 0x0080 +#define PM_EVENT_USER 0x0100 +#define PM_EVENT_REMOTE 0x0200 +#define PM_EVENT_AUTO 0x0400 + +#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) +#define PM_EVENT_USER_SUSPEND (PM_EVENT_USER | PM_EVENT_SUSPEND) +#define PM_EVENT_USER_RESUME (PM_EVENT_USER | PM_EVENT_RESUME) +#define PM_EVENT_REMOTE_RESUME (PM_EVENT_REMOTE | PM_EVENT_RESUME) +#define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND) +#define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) + +#define PMSG_INVALID ((struct pm_message){ .event = PM_EVENT_INVALID, }) +#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) +#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) +#define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) +#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) +#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, }) +#define PMSG_RESUME ((struct pm_message){ .event = PM_EVENT_RESUME, }) +#define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) +#define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) +#define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) +#define PMSG_USER_SUSPEND ((struct pm_message) \ + { .event = PM_EVENT_USER_SUSPEND, }) +#define PMSG_USER_RESUME ((struct pm_message) \ + { .event = PM_EVENT_USER_RESUME, }) +#define PMSG_REMOTE_RESUME ((struct pm_message) \ + { .event = PM_EVENT_REMOTE_RESUME, }) +#define PMSG_AUTO_SUSPEND ((struct pm_message) \ + { .event = PM_EVENT_AUTO_SUSPEND, }) +#define PMSG_AUTO_RESUME ((struct pm_message) \ + { .event = PM_EVENT_AUTO_RESUME, }) + +#define PMSG_IS_AUTO(msg) (((msg).event & PM_EVENT_AUTO) != 0) + +/* + * Device run-time power management status. + * + * These status labels are used internally by the PM core to indicate the + * current status of a device with respect to the PM core operations. They do + * not reflect the actual power state of the device or its status as seen by the + * driver. + * + * RPM_ACTIVE Device is fully operational. Indicates that the device + * bus type's ->runtime_resume() callback has completed + * successfully. + * + * RPM_SUSPENDED Device bus type's ->runtime_suspend() callback has + * completed successfully. The device is regarded as + * suspended. + * + * RPM_RESUMING Device bus type's ->runtime_resume() callback is being + * executed. + * + * RPM_SUSPENDING Device bus type's ->runtime_suspend() callback is being + * executed. + */ + +enum rpm_status { + RPM_ACTIVE = 0, + RPM_RESUMING, + RPM_SUSPENDED, + RPM_SUSPENDING, +}; + +/* + * Device run-time power management request types. + * + * RPM_REQ_NONE Do nothing. + * + * RPM_REQ_IDLE Run the device bus type's ->runtime_idle() callback + * + * RPM_REQ_SUSPEND Run the device bus type's ->runtime_suspend() callback + * + * RPM_REQ_AUTOSUSPEND Same as RPM_REQ_SUSPEND, but not until the device has + * been inactive for as long as power.autosuspend_delay + * + * RPM_REQ_RESUME Run the device bus type's ->runtime_resume() callback + */ + +enum rpm_request { + RPM_REQ_NONE = 0, + RPM_REQ_IDLE, + RPM_REQ_SUSPEND, + RPM_REQ_AUTOSUSPEND, + RPM_REQ_RESUME, +}; + +struct wakeup_source; +struct wake_irq; +struct pm_domain_data; + +struct pm_subsys_data { + spinlock_t lock; + unsigned int refcount; +#ifdef CONFIG_PM_CLK + struct list_head clock_list; +#endif +#ifdef CONFIG_PM_GENERIC_DOMAINS + struct pm_domain_data *domain_data; +#endif +}; + +/* + * Driver flags to control system suspend/resume behavior. + * + * These flags can be set by device drivers at the probe time. They need not be + * cleared by the drivers as the driver core will take care of that. + * + * NEVER_SKIP: Do not skip all system suspend/resume callbacks for the device. + * SMART_PREPARE: Check the return value of the driver's ->prepare callback. + * SMART_SUSPEND: No need to resume the device from runtime suspend. + * LEAVE_SUSPENDED: Avoid resuming the device during system resume if possible. + * + * Setting SMART_PREPARE instructs bus types and PM domains which may want + * system suspend/resume callbacks to be skipped for the device to return 0 from + * their ->prepare callbacks if the driver's ->prepare callback returns 0 (in + * other words, the system suspend/resume callbacks can only be skipped for the + * device if its driver doesn't object against that). This flag has no effect + * if NEVER_SKIP is set. + * + * Setting SMART_SUSPEND instructs bus types and PM domains which may want to + * runtime resume the device upfront during system suspend that doing so is not + * necessary from the driver's perspective. It also may cause them to skip + * invocations of the ->suspend_late and ->suspend_noirq callbacks provided by + * the driver if they decide to leave the device in runtime suspend. + * + * Setting LEAVE_SUSPENDED informs the PM core and middle-layer code that the + * driver prefers the device to be left in suspend after system resume. + */ +#define DPM_FLAG_NEVER_SKIP BIT(0) +#define DPM_FLAG_SMART_PREPARE BIT(1) +#define DPM_FLAG_SMART_SUSPEND BIT(2) +#define DPM_FLAG_LEAVE_SUSPENDED BIT(3) + +struct dev_pm_info { + pm_message_t power_state; + unsigned int can_wakeup:1; + unsigned int async_suspend:1; + bool in_dpm_list:1; /* Owned by the PM core */ + bool is_prepared:1; /* Owned by the PM core */ + bool is_suspended:1; /* Ditto */ + bool is_noirq_suspended:1; + bool is_late_suspended:1; + bool no_pm:1; + bool early_init:1; /* Owned by the PM core */ + bool direct_complete:1; /* Owned by the PM core */ + u32 driver_flags; + spinlock_t lock; +#ifdef CONFIG_PM_SLEEP + struct list_head entry; + struct completion completion; + struct wakeup_source *wakeup; + bool wakeup_path:1; + bool syscore:1; + bool no_pm_callbacks:1; /* Owned by the PM core */ + unsigned int must_resume:1; /* Owned by the PM core */ + unsigned int may_skip_resume:1; /* Set by subsystems */ +#else + unsigned int should_wakeup:1; +#endif +#ifdef CONFIG_PM + struct hrtimer suspend_timer; + unsigned long timer_expires; + struct work_struct work; + wait_queue_head_t wait_queue; + struct wake_irq *wakeirq; + atomic_t usage_count; + atomic_t child_count; + unsigned int disable_depth:3; + unsigned int idle_notification:1; + unsigned int request_pending:1; + unsigned int deferred_resume:1; + unsigned int runtime_auto:1; + bool ignore_children:1; + unsigned int no_callbacks:1; + unsigned int irq_safe:1; + unsigned int use_autosuspend:1; + unsigned int timer_autosuspends:1; + unsigned int memalloc_noio:1; + unsigned int links_count; + enum rpm_request request; + enum rpm_status runtime_status; + int runtime_error; + int autosuspend_delay; + u64 last_busy; + u64 active_time; + u64 suspended_time; + u64 accounting_timestamp; +#endif + struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ + void (*set_latency_tolerance)(struct device *, s32); + struct dev_pm_qos *qos; +}; + +extern int dev_pm_get_subsys_data(struct device *dev); +extern void dev_pm_put_subsys_data(struct device *dev); + +/** + * struct dev_pm_domain - power management domain representation. + * + * @ops: Power management operations associated with this domain. + * @detach: Called when removing a device from the domain. + * @activate: Called before executing probe routines for bus types and drivers. + * @sync: Called after successful driver probe. + * @dismiss: Called after unsuccessful driver probe and after driver removal. + * + * Power domains provide callbacks that are executed during system suspend, + * hibernation, system resume and during runtime PM transitions instead of + * subsystem-level and driver-level callbacks. + */ +struct dev_pm_domain { + struct dev_pm_ops ops; + void (*detach)(struct device *dev, bool power_off); + int (*activate)(struct device *dev); + void (*sync)(struct device *dev); + void (*dismiss)(struct device *dev); +}; + +/* + * The PM_EVENT_ messages are also used by drivers implementing the legacy + * suspend framework, based on the ->suspend() and ->resume() callbacks common + * for suspend and hibernation transitions, according to the rules below. + */ + +/* Necessary, because several drivers use PM_EVENT_PRETHAW */ +#define PM_EVENT_PRETHAW PM_EVENT_QUIESCE + +/* + * One transition is triggered by resume(), after a suspend() call; the + * message is implicit: + * + * ON Driver starts working again, responding to hardware events + * and software requests. The hardware may have gone through + * a power-off reset, or it may have maintained state from the + * previous suspend() which the driver will rely on while + * resuming. On most platforms, there are no restrictions on + * availability of resources like clocks during resume(). + * + * Other transitions are triggered by messages sent using suspend(). All + * these transitions quiesce the driver, so that I/O queues are inactive. + * That commonly entails turning off IRQs and DMA; there may be rules + * about how to quiesce that are specific to the bus or the device's type. + * (For example, network drivers mark the link state.) Other details may + * differ according to the message: + * + * SUSPEND Quiesce, enter a low power device state appropriate for + * the upcoming system state (such as PCI_D3hot), and enable + * wakeup events as appropriate. + * + * HIBERNATE Enter a low power device state appropriate for the hibernation + * state (eg. ACPI S4) and enable wakeup events as appropriate. + * + * FREEZE Quiesce operations so that a consistent image can be saved; + * but do NOT otherwise enter a low power device state, and do + * NOT emit system wakeup events. + * + * PRETHAW Quiesce as if for FREEZE; additionally, prepare for restoring + * the system from a snapshot taken after an earlier FREEZE. + * Some drivers will need to reset their hardware state instead + * of preserving it, to ensure that it's never mistaken for the + * state which that earlier snapshot had set up. + * + * A minimally power-aware driver treats all messages as SUSPEND, fully + * reinitializes its device during resume() -- whether or not it was reset + * during the suspend/resume cycle -- and can't issue wakeup events. + * + * More power-aware drivers may also use low power states at runtime as + * well as during system sleep states like PM_SUSPEND_STANDBY. They may + * be able to use wakeup events to exit from runtime low-power states, + * or from system low-power states such as standby or suspend-to-RAM. + */ + +#ifdef CONFIG_PM_SLEEP +extern void device_pm_lock(void); +extern void dpm_resume_start(pm_message_t state); +extern void dpm_resume_end(pm_message_t state); +extern void dpm_resume_noirq(pm_message_t state); +extern void dpm_resume_early(pm_message_t state); +extern void dpm_resume(pm_message_t state); +extern void dpm_complete(pm_message_t state); + +extern void device_pm_unlock(void); +extern int dpm_suspend_end(pm_message_t state); +extern int dpm_suspend_start(pm_message_t state); +extern int dpm_suspend_noirq(pm_message_t state); +extern int dpm_suspend_late(pm_message_t state); +extern int dpm_suspend(pm_message_t state); +extern int dpm_prepare(pm_message_t state); + +extern void __suspend_report_result(const char *function, void *fn, int ret); + +#define suspend_report_result(fn, ret) \ + do { \ + __suspend_report_result(__func__, fn, ret); \ + } while (0) + +extern int device_pm_wait_for_dev(struct device *sub, struct device *dev); +extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)); + +extern int pm_generic_prepare(struct device *dev); +extern int pm_generic_suspend_late(struct device *dev); +extern int pm_generic_suspend_noirq(struct device *dev); +extern int pm_generic_suspend(struct device *dev); +extern int pm_generic_resume_early(struct device *dev); +extern int pm_generic_resume_noirq(struct device *dev); +extern int pm_generic_resume(struct device *dev); +extern int pm_generic_freeze_noirq(struct device *dev); +extern int pm_generic_freeze_late(struct device *dev); +extern int pm_generic_freeze(struct device *dev); +extern int pm_generic_thaw_noirq(struct device *dev); +extern int pm_generic_thaw_early(struct device *dev); +extern int pm_generic_thaw(struct device *dev); +extern int pm_generic_restore_noirq(struct device *dev); +extern int pm_generic_restore_early(struct device *dev); +extern int pm_generic_restore(struct device *dev); +extern int pm_generic_poweroff_noirq(struct device *dev); +extern int pm_generic_poweroff_late(struct device *dev); +extern int pm_generic_poweroff(struct device *dev); +extern void pm_generic_complete(struct device *dev); + +extern bool dev_pm_may_skip_resume(struct device *dev); +extern bool dev_pm_smart_suspend_and_suspended(struct device *dev); + +#else /* !CONFIG_PM_SLEEP */ + +#define device_pm_lock() do {} while (0) +#define device_pm_unlock() do {} while (0) + +static inline int dpm_suspend_start(pm_message_t state) +{ + return 0; +} + +#define suspend_report_result(fn, ret) do {} while (0) + +static inline int device_pm_wait_for_dev(struct device *a, struct device *b) +{ + return 0; +} + +static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ +} + +#define pm_generic_prepare NULL +#define pm_generic_suspend_late NULL +#define pm_generic_suspend_noirq NULL +#define pm_generic_suspend NULL +#define pm_generic_resume_early NULL +#define pm_generic_resume_noirq NULL +#define pm_generic_resume NULL +#define pm_generic_freeze_noirq NULL +#define pm_generic_freeze_late NULL +#define pm_generic_freeze NULL +#define pm_generic_thaw_noirq NULL +#define pm_generic_thaw_early NULL +#define pm_generic_thaw NULL +#define pm_generic_restore_noirq NULL +#define pm_generic_restore_early NULL +#define pm_generic_restore NULL +#define pm_generic_poweroff_noirq NULL +#define pm_generic_poweroff_late NULL +#define pm_generic_poweroff NULL +#define pm_generic_complete NULL +#endif /* !CONFIG_PM_SLEEP */ + +/* How to reorder dpm_list after device_move() */ +enum dpm_order { + DPM_ORDER_NONE, + DPM_ORDER_DEV_AFTER_PARENT, + DPM_ORDER_PARENT_BEFORE_DEV, + DPM_ORDER_DEV_LAST, +}; + +#endif /* _LINUX_PM_H */ diff --git a/t/tree/include/linux/pm_wakeup.h b/t/tree/include/linux/pm_wakeup.h new file mode 100644 index 00000000..661efa02 --- /dev/null +++ b/t/tree/include/linux/pm_wakeup.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * pm_wakeup.h - Power management wakeup interface + * + * Copyright (C) 2008 Alan Stern + * Copyright (C) 2010 Rafael J. Wysocki, Novell Inc. + */ + +#ifndef _LINUX_PM_WAKEUP_H +#define _LINUX_PM_WAKEUP_H + +#ifndef _DEVICE_H_ +# error "please don't include this file directly" +#endif + +#include + +struct wake_irq; + +/** + * struct wakeup_source - Representation of wakeup sources + * + * @name: Name of the wakeup source + * @id: Wakeup source id + * @entry: Wakeup source list entry + * @lock: Wakeup source lock + * @wakeirq: Optional device specific wakeirq + * @timer: Wakeup timer list + * @timer_expires: Wakeup timer expiration + * @total_time: Total time this wakeup source has been active. + * @max_time: Maximum time this wakeup source has been continuously active. + * @last_time: Monotonic clock when the wakeup source's was touched last time. + * @prevent_sleep_time: Total time this source has been preventing autosleep. + * @event_count: Number of signaled wakeup events. + * @active_count: Number of times the wakeup source was activated. + * @relax_count: Number of times the wakeup source was deactivated. + * @expire_count: Number of times the wakeup source's timeout has expired. + * @wakeup_count: Number of times the wakeup source might abort suspend. + * @dev: Struct device for sysfs statistics about the wakeup source. + * @active: Status of the wakeup source. + * @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time. + */ +struct wakeup_source { + const char *name; + int id; + struct list_head entry; + spinlock_t lock; + struct wake_irq *wakeirq; + struct timer_list timer; + unsigned long timer_expires; + ktime_t total_time; + ktime_t max_time; + ktime_t last_time; + ktime_t start_prevent_time; + ktime_t prevent_sleep_time; + unsigned long event_count; + unsigned long active_count; + unsigned long relax_count; + unsigned long expire_count; + unsigned long wakeup_count; + struct device *dev; + bool active:1; + bool autosleep_enabled:1; +}; + +#ifdef CONFIG_PM_SLEEP + +/* + * Changes to device_may_wakeup take effect on the next pm state change. + */ + +static inline bool device_can_wakeup(struct device *dev) +{ + return dev->power.can_wakeup; +} + +static inline bool device_may_wakeup(struct device *dev) +{ + return dev->power.can_wakeup && !!dev->power.wakeup; +} + +static inline void device_set_wakeup_path(struct device *dev) +{ + dev->power.wakeup_path = true; +} + +/* drivers/base/power/wakeup.c */ +extern struct wakeup_source *wakeup_source_create(const char *name); +extern void wakeup_source_destroy(struct wakeup_source *ws); +extern void wakeup_source_add(struct wakeup_source *ws); +extern void wakeup_source_remove(struct wakeup_source *ws); +extern struct wakeup_source *wakeup_source_register(struct device *dev, + const char *name); +extern void wakeup_source_unregister(struct wakeup_source *ws); +extern int device_wakeup_enable(struct device *dev); +extern int device_wakeup_disable(struct device *dev); +extern void device_set_wakeup_capable(struct device *dev, bool capable); +extern int device_init_wakeup(struct device *dev, bool val); +extern int device_set_wakeup_enable(struct device *dev, bool enable); +extern void __pm_stay_awake(struct wakeup_source *ws); +extern void pm_stay_awake(struct device *dev); +extern void __pm_relax(struct wakeup_source *ws); +extern void pm_relax(struct device *dev); +extern void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard); +extern void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard); + +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_set_wakeup_capable(struct device *dev, bool capable) +{ + dev->power.can_wakeup = capable; +} + +static inline bool device_can_wakeup(struct device *dev) +{ + return dev->power.can_wakeup; +} + +static inline struct wakeup_source *wakeup_source_create(const char *name) +{ + return NULL; +} + +static inline void wakeup_source_destroy(struct wakeup_source *ws) {} + +static inline void wakeup_source_add(struct wakeup_source *ws) {} + +static inline void wakeup_source_remove(struct wakeup_source *ws) {} + +static inline struct wakeup_source *wakeup_source_register(struct device *dev, + const char *name) +{ + return NULL; +} + +static inline void wakeup_source_unregister(struct wakeup_source *ws) {} + +static inline int device_wakeup_enable(struct device *dev) +{ + dev->power.should_wakeup = true; + return 0; +} + +static inline int device_wakeup_disable(struct device *dev) +{ + dev->power.should_wakeup = false; + return 0; +} + +static inline int device_set_wakeup_enable(struct device *dev, bool enable) +{ + dev->power.should_wakeup = enable; + return 0; +} + +static inline int device_init_wakeup(struct device *dev, bool val) +{ + device_set_wakeup_capable(dev, val); + device_set_wakeup_enable(dev, val); + return 0; +} + +static inline bool device_may_wakeup(struct device *dev) +{ + return dev->power.can_wakeup && dev->power.should_wakeup; +} + +static inline void device_set_wakeup_path(struct device *dev) {} + +static inline void __pm_stay_awake(struct wakeup_source *ws) {} + +static inline void pm_stay_awake(struct device *dev) {} + +static inline void __pm_relax(struct wakeup_source *ws) {} + +static inline void pm_relax(struct device *dev) {} + +static inline void pm_wakeup_ws_event(struct wakeup_source *ws, + unsigned int msec, bool hard) {} + +static inline void pm_wakeup_dev_event(struct device *dev, unsigned int msec, + bool hard) {} + +#endif /* !CONFIG_PM_SLEEP */ + +static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec) +{ + return pm_wakeup_ws_event(ws, msec, false); +} + +static inline void pm_wakeup_event(struct device *dev, unsigned int msec) +{ + return pm_wakeup_dev_event(dev, msec, false); +} + +static inline void pm_wakeup_hard_event(struct device *dev) +{ + return pm_wakeup_dev_event(dev, 0, true); +} + +#endif /* _LINUX_PM_WAKEUP_H */ diff --git a/t/tree/include/linux/radix-tree.h b/t/tree/include/linux/radix-tree.h new file mode 100644 index 00000000..63e62372 --- /dev/null +++ b/t/tree/include/linux/radix-tree.h @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2001 Momchil Velikov + * Portions Copyright (C) 2001 Christoph Hellwig + * Copyright (C) 2006 Nick Piggin + * Copyright (C) 2012 Konstantin Khlebnikov + */ +#ifndef _LINUX_RADIX_TREE_H +#define _LINUX_RADIX_TREE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Keep unconverted code working */ +#define radix_tree_root xarray +#define radix_tree_node xa_node + +/* + * The bottom two bits of the slot determine how the remaining bits in the + * slot are interpreted: + * + * 00 - data pointer + * 10 - internal entry + * x1 - value entry + * + * The internal entry may be a pointer to the next level in the tree, a + * sibling entry, or an indicator that the entry in this slot has been moved + * to another location in the tree and the lookup should be restarted. While + * NULL fits the 'data pointer' pattern, it means that there is no entry in + * the tree for this index (no matter what level of the tree it is found at). + * This means that storing a NULL entry in the tree is the same as deleting + * the entry from the tree. + */ +#define RADIX_TREE_ENTRY_MASK 3UL +#define RADIX_TREE_INTERNAL_NODE 2UL + +static inline bool radix_tree_is_internal_node(void *ptr) +{ + return ((unsigned long)ptr & RADIX_TREE_ENTRY_MASK) == + RADIX_TREE_INTERNAL_NODE; +} + +/*** radix-tree API starts here ***/ + +#define RADIX_TREE_MAP_SHIFT XA_CHUNK_SHIFT +#define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) +#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) + +#define RADIX_TREE_MAX_TAGS XA_MAX_MARKS +#define RADIX_TREE_TAG_LONGS XA_MARK_LONGS + +#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long)) +#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS, \ + RADIX_TREE_MAP_SHIFT)) + +/* The IDR tag is stored in the low bits of xa_flags */ +#define ROOT_IS_IDR ((__force gfp_t)4) +/* The top bits of xa_flags are used to store the root tags */ +#define ROOT_TAG_SHIFT (__GFP_BITS_SHIFT) + +#define RADIX_TREE_INIT(name, mask) XARRAY_INIT(name, mask) + +#define RADIX_TREE(name, mask) \ + struct radix_tree_root name = RADIX_TREE_INIT(name, mask) + +#define INIT_RADIX_TREE(root, mask) xa_init_flags(root, mask) + +static inline bool radix_tree_empty(const struct radix_tree_root *root) +{ + return root->xa_head == NULL; +} + +/** + * struct radix_tree_iter - radix tree iterator state + * + * @index: index of current slot + * @next_index: one beyond the last index for this chunk + * @tags: bit-mask for tag-iterating + * @node: node that contains current slot + * + * This radix tree iterator works in terms of "chunks" of slots. A chunk is a + * subinterval of slots contained within one radix tree leaf node. It is + * described by a pointer to its first slot and a struct radix_tree_iter + * which holds the chunk's position in the tree and its size. For tagged + * iteration radix_tree_iter also holds the slots' bit-mask for one chosen + * radix tree tag. + */ +struct radix_tree_iter { + unsigned long index; + unsigned long next_index; + unsigned long tags; + struct radix_tree_node *node; +}; + +/** + * Radix-tree synchronization + * + * The radix-tree API requires that users provide all synchronisation (with + * specific exceptions, noted below). + * + * Synchronization of access to the data items being stored in the tree, and + * management of their lifetimes must be completely managed by API users. + * + * For API usage, in general, + * - any function _modifying_ the tree or tags (inserting or deleting + * items, setting or clearing tags) must exclude other modifications, and + * exclude any functions reading the tree. + * - any function _reading_ the tree or tags (looking up items or tags, + * gang lookups) must exclude modifications to the tree, but may occur + * concurrently with other readers. + * + * The notable exceptions to this rule are the following functions: + * __radix_tree_lookup + * radix_tree_lookup + * radix_tree_lookup_slot + * radix_tree_tag_get + * radix_tree_gang_lookup + * radix_tree_gang_lookup_tag + * radix_tree_gang_lookup_tag_slot + * radix_tree_tagged + * + * The first 7 functions are able to be called locklessly, using RCU. The + * caller must ensure calls to these functions are made within rcu_read_lock() + * regions. Other readers (lock-free or otherwise) and modifications may be + * running concurrently. + * + * It is still required that the caller manage the synchronization and lifetimes + * of the items. So if RCU lock-free lookups are used, typically this would mean + * that the items have their own locks, or are amenable to lock-free access; and + * that the items are freed by RCU (or only freed after having been deleted from + * the radix tree *and* a synchronize_rcu() grace period). + * + * (Note, rcu_assign_pointer and rcu_dereference are not needed to control + * access to data items when inserting into or looking up from the radix tree) + * + * Note that the value returned by radix_tree_tag_get() may not be relied upon + * if only the RCU read lock is held. Functions to set/clear tags and to + * delete nodes running concurrently with it may affect its result such that + * two consecutive reads in the same locked section may return different + * values. If reliability is required, modification functions must also be + * excluded from concurrency. + * + * radix_tree_tagged is able to be called without locking or RCU. + */ + +/** + * radix_tree_deref_slot - dereference a slot + * @slot: slot pointer, returned by radix_tree_lookup_slot + * + * For use with radix_tree_lookup_slot(). Caller must hold tree at least read + * locked across slot lookup and dereference. Not required if write lock is + * held (ie. items cannot be concurrently inserted). + * + * radix_tree_deref_retry must be used to confirm validity of the pointer if + * only the read lock is held. + * + * Return: entry stored in that slot. + */ +static inline void *radix_tree_deref_slot(void __rcu **slot) +{ + return rcu_dereference(*slot); +} + +/** + * radix_tree_deref_slot_protected - dereference a slot with tree lock held + * @slot: slot pointer, returned by radix_tree_lookup_slot + * + * Similar to radix_tree_deref_slot. The caller does not hold the RCU read + * lock but it must hold the tree lock to prevent parallel updates. + * + * Return: entry stored in that slot. + */ +static inline void *radix_tree_deref_slot_protected(void __rcu **slot, + spinlock_t *treelock) +{ + return rcu_dereference_protected(*slot, lockdep_is_held(treelock)); +} + +/** + * radix_tree_deref_retry - check radix_tree_deref_slot + * @arg: pointer returned by radix_tree_deref_slot + * Returns: 0 if retry is not required, otherwise retry is required + * + * radix_tree_deref_retry must be used with radix_tree_deref_slot. + */ +static inline int radix_tree_deref_retry(void *arg) +{ + return unlikely(radix_tree_is_internal_node(arg)); +} + +/** + * radix_tree_exception - radix_tree_deref_slot returned either exception? + * @arg: value returned by radix_tree_deref_slot + * Returns: 0 if well-aligned pointer, non-0 if either kind of exception. + */ +static inline int radix_tree_exception(void *arg) +{ + return unlikely((unsigned long)arg & RADIX_TREE_ENTRY_MASK); +} + +int radix_tree_insert(struct radix_tree_root *, unsigned long index, + void *); +void *__radix_tree_lookup(const struct radix_tree_root *, unsigned long index, + struct radix_tree_node **nodep, void __rcu ***slotp); +void *radix_tree_lookup(const struct radix_tree_root *, unsigned long); +void __rcu **radix_tree_lookup_slot(const struct radix_tree_root *, + unsigned long index); +void __radix_tree_replace(struct radix_tree_root *, struct radix_tree_node *, + void __rcu **slot, void *entry); +void radix_tree_iter_replace(struct radix_tree_root *, + const struct radix_tree_iter *, void __rcu **slot, void *entry); +void radix_tree_replace_slot(struct radix_tree_root *, + void __rcu **slot, void *entry); +void radix_tree_iter_delete(struct radix_tree_root *, + struct radix_tree_iter *iter, void __rcu **slot); +void *radix_tree_delete_item(struct radix_tree_root *, unsigned long, void *); +void *radix_tree_delete(struct radix_tree_root *, unsigned long); +unsigned int radix_tree_gang_lookup(const struct radix_tree_root *, + void **results, unsigned long first_index, + unsigned int max_items); +int radix_tree_preload(gfp_t gfp_mask); +int radix_tree_maybe_preload(gfp_t gfp_mask); +void radix_tree_init(void); +void *radix_tree_tag_set(struct radix_tree_root *, + unsigned long index, unsigned int tag); +void *radix_tree_tag_clear(struct radix_tree_root *, + unsigned long index, unsigned int tag); +int radix_tree_tag_get(const struct radix_tree_root *, + unsigned long index, unsigned int tag); +void radix_tree_iter_tag_clear(struct radix_tree_root *, + const struct radix_tree_iter *iter, unsigned int tag); +unsigned int radix_tree_gang_lookup_tag(const struct radix_tree_root *, + void **results, unsigned long first_index, + unsigned int max_items, unsigned int tag); +unsigned int radix_tree_gang_lookup_tag_slot(const struct radix_tree_root *, + void __rcu ***results, unsigned long first_index, + unsigned int max_items, unsigned int tag); +int radix_tree_tagged(const struct radix_tree_root *, unsigned int tag); + +static inline void radix_tree_preload_end(void) +{ + preempt_enable(); +} + +void __rcu **idr_get_free(struct radix_tree_root *root, + struct radix_tree_iter *iter, gfp_t gfp, + unsigned long max); + +enum { + RADIX_TREE_ITER_TAG_MASK = 0x0f, /* tag index in lower nybble */ + RADIX_TREE_ITER_TAGGED = 0x10, /* lookup tagged slots */ + RADIX_TREE_ITER_CONTIG = 0x20, /* stop at first hole */ +}; + +/** + * radix_tree_iter_init - initialize radix tree iterator + * + * @iter: pointer to iterator state + * @start: iteration starting index + * Returns: NULL + */ +static __always_inline void __rcu ** +radix_tree_iter_init(struct radix_tree_iter *iter, unsigned long start) +{ + /* + * Leave iter->tags uninitialized. radix_tree_next_chunk() will fill it + * in the case of a successful tagged chunk lookup. If the lookup was + * unsuccessful or non-tagged then nobody cares about ->tags. + * + * Set index to zero to bypass next_index overflow protection. + * See the comment in radix_tree_next_chunk() for details. + */ + iter->index = 0; + iter->next_index = start; + return NULL; +} + +/** + * radix_tree_next_chunk - find next chunk of slots for iteration + * + * @root: radix tree root + * @iter: iterator state + * @flags: RADIX_TREE_ITER_* flags and tag index + * Returns: pointer to chunk first slot, or NULL if there no more left + * + * This function looks up the next chunk in the radix tree starting from + * @iter->next_index. It returns a pointer to the chunk's first slot. + * Also it fills @iter with data about chunk: position in the tree (index), + * its end (next_index), and constructs a bit mask for tagged iterating (tags). + */ +void __rcu **radix_tree_next_chunk(const struct radix_tree_root *, + struct radix_tree_iter *iter, unsigned flags); + +/** + * radix_tree_iter_lookup - look up an index in the radix tree + * @root: radix tree root + * @iter: iterator state + * @index: key to look up + * + * If @index is present in the radix tree, this function returns the slot + * containing it and updates @iter to describe the entry. If @index is not + * present, it returns NULL. + */ +static inline void __rcu ** +radix_tree_iter_lookup(const struct radix_tree_root *root, + struct radix_tree_iter *iter, unsigned long index) +{ + radix_tree_iter_init(iter, index); + return radix_tree_next_chunk(root, iter, RADIX_TREE_ITER_CONTIG); +} + +/** + * radix_tree_iter_retry - retry this chunk of the iteration + * @iter: iterator state + * + * If we iterate over a tree protected only by the RCU lock, a race + * against deletion or creation may result in seeing a slot for which + * radix_tree_deref_retry() returns true. If so, call this function + * and continue the iteration. + */ +static inline __must_check +void __rcu **radix_tree_iter_retry(struct radix_tree_iter *iter) +{ + iter->next_index = iter->index; + iter->tags = 0; + return NULL; +} + +static inline unsigned long +__radix_tree_iter_add(struct radix_tree_iter *iter, unsigned long slots) +{ + return iter->index + slots; +} + +/** + * radix_tree_iter_resume - resume iterating when the chunk may be invalid + * @slot: pointer to current slot + * @iter: iterator state + * Returns: New slot pointer + * + * If the iterator needs to release then reacquire a lock, the chunk may + * have been invalidated by an insertion or deletion. Call this function + * before releasing the lock to continue the iteration from the next index. + */ +void __rcu **__must_check radix_tree_iter_resume(void __rcu **slot, + struct radix_tree_iter *iter); + +/** + * radix_tree_chunk_size - get current chunk size + * + * @iter: pointer to radix tree iterator + * Returns: current chunk size + */ +static __always_inline long +radix_tree_chunk_size(struct radix_tree_iter *iter) +{ + return iter->next_index - iter->index; +} + +/** + * radix_tree_next_slot - find next slot in chunk + * + * @slot: pointer to current slot + * @iter: pointer to interator state + * @flags: RADIX_TREE_ITER_*, should be constant + * Returns: pointer to next slot, or NULL if there no more left + * + * This function updates @iter->index in the case of a successful lookup. + * For tagged lookup it also eats @iter->tags. + * + * There are several cases where 'slot' can be passed in as NULL to this + * function. These cases result from the use of radix_tree_iter_resume() or + * radix_tree_iter_retry(). In these cases we don't end up dereferencing + * 'slot' because either: + * a) we are doing tagged iteration and iter->tags has been set to 0, or + * b) we are doing non-tagged iteration, and iter->index and iter->next_index + * have been set up so that radix_tree_chunk_size() returns 1 or 0. + */ +static __always_inline void __rcu **radix_tree_next_slot(void __rcu **slot, + struct radix_tree_iter *iter, unsigned flags) +{ + if (flags & RADIX_TREE_ITER_TAGGED) { + iter->tags >>= 1; + if (unlikely(!iter->tags)) + return NULL; + if (likely(iter->tags & 1ul)) { + iter->index = __radix_tree_iter_add(iter, 1); + slot++; + goto found; + } + if (!(flags & RADIX_TREE_ITER_CONTIG)) { + unsigned offset = __ffs(iter->tags); + + iter->tags >>= offset++; + iter->index = __radix_tree_iter_add(iter, offset); + slot += offset; + goto found; + } + } else { + long count = radix_tree_chunk_size(iter); + + while (--count > 0) { + slot++; + iter->index = __radix_tree_iter_add(iter, 1); + + if (likely(*slot)) + goto found; + if (flags & RADIX_TREE_ITER_CONTIG) { + /* forbid switching to the next chunk */ + iter->next_index = 0; + break; + } + } + } + return NULL; + + found: + return slot; +} + +/** + * radix_tree_for_each_slot - iterate over non-empty slots + * + * @slot: the void** variable for pointer to slot + * @root: the struct radix_tree_root pointer + * @iter: the struct radix_tree_iter pointer + * @start: iteration starting index + * + * @slot points to radix tree slot, @iter->index contains its index. + */ +#define radix_tree_for_each_slot(slot, root, iter, start) \ + for (slot = radix_tree_iter_init(iter, start) ; \ + slot || (slot = radix_tree_next_chunk(root, iter, 0)) ; \ + slot = radix_tree_next_slot(slot, iter, 0)) + +/** + * radix_tree_for_each_tagged - iterate over tagged slots + * + * @slot: the void** variable for pointer to slot + * @root: the struct radix_tree_root pointer + * @iter: the struct radix_tree_iter pointer + * @start: iteration starting index + * @tag: tag index + * + * @slot points to radix tree slot, @iter->index contains its index. + */ +#define radix_tree_for_each_tagged(slot, root, iter, start, tag) \ + for (slot = radix_tree_iter_init(iter, start) ; \ + slot || (slot = radix_tree_next_chunk(root, iter, \ + RADIX_TREE_ITER_TAGGED | tag)) ; \ + slot = radix_tree_next_slot(slot, iter, \ + RADIX_TREE_ITER_TAGGED | tag)) + +#endif /* _LINUX_RADIX_TREE_H */ diff --git a/t/tree/include/linux/rbtree.h b/t/tree/include/linux/rbtree.h new file mode 100644 index 00000000..1fd61a9a --- /dev/null +++ b/t/tree/include/linux/rbtree.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + I know it's not the cleaner way, but in C (not in C++) to get + performances and genericity... + + See Documentation/rbtree.txt for documentation and samples. +*/ + +#ifndef _LINUX_RBTREE_H +#define _LINUX_RBTREE_H + +#include +#include +#include + +struct rb_node { + unsigned long __rb_parent_color; + struct rb_node *rb_right; + struct rb_node *rb_left; +} __attribute__((aligned(sizeof(long)))); + /* The alignment might seem pointless, but allegedly CRIS needs it */ + +struct rb_root { + struct rb_node *rb_node; +}; + +#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) + +#define RB_ROOT (struct rb_root) { NULL, } +#define rb_entry(ptr, type, member) container_of(ptr, type, member) + +#define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == NULL) + +/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */ +#define RB_EMPTY_NODE(node) \ + ((node)->__rb_parent_color == (unsigned long)(node)) +#define RB_CLEAR_NODE(node) \ + ((node)->__rb_parent_color = (unsigned long)(node)) + + +extern void rb_insert_color(struct rb_node *, struct rb_root *); +extern void rb_erase(struct rb_node *, struct rb_root *); + + +/* Find logical next and previous nodes in a tree */ +extern struct rb_node *rb_next(const struct rb_node *); +extern struct rb_node *rb_prev(const struct rb_node *); +extern struct rb_node *rb_first(const struct rb_root *); +extern struct rb_node *rb_last(const struct rb_root *); + +/* Postorder iteration - always visit the parent after its children */ +extern struct rb_node *rb_first_postorder(const struct rb_root *); +extern struct rb_node *rb_next_postorder(const struct rb_node *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); +extern void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, + struct rb_root *root); + +static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, + struct rb_node **rb_link) +{ + node->__rb_parent_color = (unsigned long)parent; + node->rb_left = node->rb_right = NULL; + + *rb_link = node; +} + +static inline void rb_link_node_rcu(struct rb_node *node, struct rb_node *parent, + struct rb_node **rb_link) +{ + node->__rb_parent_color = (unsigned long)parent; + node->rb_left = node->rb_right = NULL; + + rcu_assign_pointer(*rb_link, node); +} + +#define rb_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? rb_entry(____ptr, type, member) : NULL; \ + }) + +/** + * rbtree_postorder_for_each_entry_safe - iterate in post-order over rb_root of + * given type allowing the backing memory of @pos to be invalidated + * + * @pos: the 'type *' to use as a loop cursor. + * @n: another 'type *' to use as temporary storage + * @root: 'rb_root *' of the rbtree. + * @field: the name of the rb_node field within 'type'. + * + * rbtree_postorder_for_each_entry_safe() provides a similar guarantee as + * list_for_each_entry_safe() and allows the iteration to continue independent + * of changes to @pos by the body of the loop. + * + * Note, however, that it cannot handle other modifications that re-order the + * rbtree it is iterating over. This includes calling rb_erase() on @pos, as + * rb_erase() may rebalance the tree, causing us to miss some nodes. + */ +#define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \ + for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \ + pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \ + typeof(*pos), field); 1; }); \ + pos = n) + +/* + * Leftmost-cached rbtrees. + * + * We do not cache the rightmost node based on footprint + * size vs number of potential users that could benefit + * from O(1) rb_last(). Just not worth it, users that want + * this feature can always implement the logic explicitly. + * Furthermore, users that want to cache both pointers may + * find it a bit asymmetric, but that's ok. + */ +struct rb_root_cached { + struct rb_root rb_root; + struct rb_node *rb_leftmost; +}; + +#define RB_ROOT_CACHED (struct rb_root_cached) { {NULL, }, NULL } + +/* Same as rb_first(), but O(1) */ +#define rb_first_cached(root) (root)->rb_leftmost + +static inline void rb_insert_color_cached(struct rb_node *node, + struct rb_root_cached *root, + bool leftmost) +{ + if (leftmost) + root->rb_leftmost = node; + rb_insert_color(node, &root->rb_root); +} + +static inline void rb_erase_cached(struct rb_node *node, + struct rb_root_cached *root) +{ + if (root->rb_leftmost == node) + root->rb_leftmost = rb_next(node); + rb_erase(node, &root->rb_root); +} + +static inline void rb_replace_node_cached(struct rb_node *victim, + struct rb_node *new, + struct rb_root_cached *root) +{ + if (root->rb_leftmost == victim) + root->rb_leftmost = new; + rb_replace_node(victim, new, &root->rb_root); +} + +#endif /* _LINUX_RBTREE_H */ diff --git a/t/tree/include/linux/rcu_node_tree.h b/t/tree/include/linux/rcu_node_tree.h new file mode 100644 index 00000000..b8e094b1 --- /dev/null +++ b/t/tree/include/linux/rcu_node_tree.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * RCU node combining tree definitions. These are used to compute + * global attributes while avoiding common-case global contention. A key + * property that these computations rely on is a tournament-style approach + * where only one of the tasks contending a lower level in the tree need + * advance to the next higher level. If properly configured, this allows + * unlimited scalability while maintaining a constant level of contention + * on the root node. + * + * This seemingly RCU-private file must be available to SRCU users + * because the size of the TREE SRCU srcu_struct structure depends + * on these definitions. + * + * Copyright IBM Corporation, 2017 + * + * Author: Paul E. McKenney + */ + +#ifndef __LINUX_RCU_NODE_TREE_H +#define __LINUX_RCU_NODE_TREE_H + +/* + * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and + * CONFIG_RCU_FANOUT_LEAF. + * In theory, it should be possible to add more levels straightforwardly. + * In practice, this did work well going from three levels to four. + * Of course, your mileage may vary. + */ + +#ifdef CONFIG_RCU_FANOUT +#define RCU_FANOUT CONFIG_RCU_FANOUT +#else /* #ifdef CONFIG_RCU_FANOUT */ +# ifdef CONFIG_64BIT +# define RCU_FANOUT 64 +# else +# define RCU_FANOUT 32 +# endif +#endif /* #else #ifdef CONFIG_RCU_FANOUT */ + +#ifdef CONFIG_RCU_FANOUT_LEAF +#define RCU_FANOUT_LEAF CONFIG_RCU_FANOUT_LEAF +#else /* #ifdef CONFIG_RCU_FANOUT_LEAF */ +#define RCU_FANOUT_LEAF 16 +#endif /* #else #ifdef CONFIG_RCU_FANOUT_LEAF */ + +#define RCU_FANOUT_1 (RCU_FANOUT_LEAF) +#define RCU_FANOUT_2 (RCU_FANOUT_1 * RCU_FANOUT) +#define RCU_FANOUT_3 (RCU_FANOUT_2 * RCU_FANOUT) +#define RCU_FANOUT_4 (RCU_FANOUT_3 * RCU_FANOUT) + +#if NR_CPUS <= RCU_FANOUT_1 +# define RCU_NUM_LVLS 1 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_NODES NUM_RCU_LVL_0 +# define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0 } +# define RCU_NODE_NAME_INIT { "rcu_node_0" } +# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0" } +#elif NR_CPUS <= RCU_FANOUT_2 +# define RCU_NUM_LVLS 2 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) +# define NUM_RCU_NODES (NUM_RCU_LVL_0 + NUM_RCU_LVL_1) +# define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0, NUM_RCU_LVL_1 } +# define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1" } +# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1" } +#elif NR_CPUS <= RCU_FANOUT_3 +# define RCU_NUM_LVLS 3 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) +# define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) +# define NUM_RCU_NODES (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2) +# define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0, NUM_RCU_LVL_1, NUM_RCU_LVL_2 } +# define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1", "rcu_node_2" } +# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2" } +#elif NR_CPUS <= RCU_FANOUT_4 +# define RCU_NUM_LVLS 4 +# define NUM_RCU_LVL_0 1 +# define NUM_RCU_LVL_1 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_3) +# define NUM_RCU_LVL_2 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_2) +# define NUM_RCU_LVL_3 DIV_ROUND_UP(NR_CPUS, RCU_FANOUT_1) +# define NUM_RCU_NODES (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3) +# define NUM_RCU_LVL_INIT { NUM_RCU_LVL_0, NUM_RCU_LVL_1, NUM_RCU_LVL_2, NUM_RCU_LVL_3 } +# define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1", "rcu_node_2", "rcu_node_3" } +# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2", "rcu_node_fqs_3" } +#else +# error "CONFIG_RCU_FANOUT insufficient for NR_CPUS" +#endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */ + +#endif /* __LINUX_RCU_NODE_TREE_H */ diff --git a/t/tree/include/linux/rcu_segcblist.h b/t/tree/include/linux/rcu_segcblist.h new file mode 100644 index 00000000..64675904 --- /dev/null +++ b/t/tree/include/linux/rcu_segcblist.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * RCU segmented callback lists + * + * This seemingly RCU-private file must be available to SRCU users + * because the size of the TREE SRCU srcu_struct structure depends + * on these definitions. + * + * Copyright IBM Corporation, 2017 + * + * Authors: Paul E. McKenney + */ + +#ifndef __INCLUDE_LINUX_RCU_SEGCBLIST_H +#define __INCLUDE_LINUX_RCU_SEGCBLIST_H + +#include +#include + +/* Simple unsegmented callback lists. */ +struct rcu_cblist { + struct rcu_head *head; + struct rcu_head **tail; + long len; + long len_lazy; +}; + +#define RCU_CBLIST_INITIALIZER(n) { .head = NULL, .tail = &n.head } + +/* Complicated segmented callback lists. ;-) */ + +/* + * Index values for segments in rcu_segcblist structure. + * + * The segments are as follows: + * + * [head, *tails[RCU_DONE_TAIL]): + * Callbacks whose grace period has elapsed, and thus can be invoked. + * [*tails[RCU_DONE_TAIL], *tails[RCU_WAIT_TAIL]): + * Callbacks waiting for the current GP from the current CPU's viewpoint. + * [*tails[RCU_WAIT_TAIL], *tails[RCU_NEXT_READY_TAIL]): + * Callbacks that arrived before the next GP started, again from + * the current CPU's viewpoint. These can be handled by the next GP. + * [*tails[RCU_NEXT_READY_TAIL], *tails[RCU_NEXT_TAIL]): + * Callbacks that might have arrived after the next GP started. + * There is some uncertainty as to when a given GP starts and + * ends, but a CPU knows the exact times if it is the one starting + * or ending the GP. Other CPUs know that the previous GP ends + * before the next one starts. + * + * Note that RCU_WAIT_TAIL cannot be empty unless RCU_NEXT_READY_TAIL is also + * empty. + * + * The ->gp_seq[] array contains the grace-period number at which the + * corresponding segment of callbacks will be ready to invoke. A given + * element of this array is meaningful only when the corresponding segment + * is non-empty, and it is never valid for RCU_DONE_TAIL (whose callbacks + * are already ready to invoke) or for RCU_NEXT_TAIL (whose callbacks have + * not yet been assigned a grace-period number). + */ +#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */ +#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */ +#define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */ +#define RCU_NEXT_TAIL 3 +#define RCU_CBLIST_NSEGS 4 + +struct rcu_segcblist { + struct rcu_head *head; + struct rcu_head **tails[RCU_CBLIST_NSEGS]; + unsigned long gp_seq[RCU_CBLIST_NSEGS]; +#ifdef CONFIG_RCU_NOCB_CPU + atomic_long_t len; +#else + long len; +#endif + long len_lazy; + u8 enabled; + u8 offloaded; +}; + +#define RCU_SEGCBLIST_INITIALIZER(n) \ +{ \ + .head = NULL, \ + .tails[RCU_DONE_TAIL] = &n.head, \ + .tails[RCU_WAIT_TAIL] = &n.head, \ + .tails[RCU_NEXT_READY_TAIL] = &n.head, \ + .tails[RCU_NEXT_TAIL] = &n.head, \ +} + +#endif /* __INCLUDE_LINUX_RCU_SEGCBLIST_H */ diff --git a/t/tree/include/linux/rcu_sync.h b/t/tree/include/linux/rcu_sync.h new file mode 100644 index 00000000..0027d4c8 --- /dev/null +++ b/t/tree/include/linux/rcu_sync.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * RCU-based infrastructure for lightweight reader-writer locking + * + * Copyright (c) 2015, Red Hat, Inc. + * + * Author: Oleg Nesterov + */ + +#ifndef _LINUX_RCU_SYNC_H_ +#define _LINUX_RCU_SYNC_H_ + +#include +#include + +/* Structure to mediate between updaters and fastpath-using readers. */ +struct rcu_sync { + int gp_state; + int gp_count; + wait_queue_head_t gp_wait; + + struct rcu_head cb_head; +}; + +/** + * rcu_sync_is_idle() - Are readers permitted to use their fastpaths? + * @rsp: Pointer to rcu_sync structure to use for synchronization + * + * Returns true if readers are permitted to use their fastpaths. Must be + * invoked within some flavor of RCU read-side critical section. + */ +static inline bool rcu_sync_is_idle(struct rcu_sync *rsp) +{ + RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(), + "suspicious rcu_sync_is_idle() usage"); + return !READ_ONCE(rsp->gp_state); /* GP_IDLE */ +} + +extern void rcu_sync_init(struct rcu_sync *); +extern void rcu_sync_enter_start(struct rcu_sync *); +extern void rcu_sync_enter(struct rcu_sync *); +extern void rcu_sync_exit(struct rcu_sync *); +extern void rcu_sync_dtor(struct rcu_sync *); + +#define __RCU_SYNC_INITIALIZER(name) { \ + .gp_state = 0, \ + .gp_count = 0, \ + .gp_wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.gp_wait), \ + } + +#define DEFINE_RCU_SYNC(name) \ + struct rcu_sync name = __RCU_SYNC_INITIALIZER(name) + +#endif /* _LINUX_RCU_SYNC_H_ */ diff --git a/t/tree/include/linux/rcupdate.h b/t/tree/include/linux/rcupdate.h new file mode 100644 index 00000000..75a2eded --- /dev/null +++ b/t/tree/include/linux/rcupdate.h @@ -0,0 +1,897 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Read-Copy Update mechanism for mutual exclusion + * + * Copyright IBM Corporation, 2001 + * + * Author: Dipankar Sarma + * + * Based on the original work by Paul McKenney + * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. + * Papers: + * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf + * http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001) + * + * For detailed explanation of Read-Copy Update mechanism see - + * http://lse.sourceforge.net/locking/rcupdate.html + * + */ + +#ifndef __LINUX_RCUPDATE_H +#define __LINUX_RCUPDATE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ULONG_CMP_GE(a, b) (ULONG_MAX / 2 >= (a) - (b)) +#define ULONG_CMP_LT(a, b) (ULONG_MAX / 2 < (a) - (b)) +#define ulong2long(a) (*(long *)(&(a))) + +/* Exported common interfaces */ +void call_rcu(struct rcu_head *head, rcu_callback_t func); +void rcu_barrier_tasks(void); +void synchronize_rcu(void); + +#ifdef CONFIG_PREEMPT_RCU + +void __rcu_read_lock(void); +void __rcu_read_unlock(void); + +/* + * Defined as a macro as it is a very low level header included from + * areas that don't even know about current. This gives the rcu_read_lock() + * nesting depth, but makes sense only if CONFIG_PREEMPT_RCU -- in other + * types of kernel builds, the rcu_read_lock() nesting depth is unknowable. + */ +#define rcu_preempt_depth() (current->rcu_read_lock_nesting) + +#else /* #ifdef CONFIG_PREEMPT_RCU */ + +static inline void __rcu_read_lock(void) +{ + preempt_disable(); +} + +static inline void __rcu_read_unlock(void) +{ + preempt_enable(); +} + +static inline int rcu_preempt_depth(void) +{ + return 0; +} + +#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ + +/* Internal to kernel */ +void rcu_init(void); +extern int rcu_scheduler_active __read_mostly; +void rcu_sched_clock_irq(int user); +void rcu_report_dead(unsigned int cpu); +void rcutree_migrate_callbacks(int cpu); + +#ifdef CONFIG_RCU_STALL_COMMON +void rcu_sysrq_start(void); +void rcu_sysrq_end(void); +#else /* #ifdef CONFIG_RCU_STALL_COMMON */ +static inline void rcu_sysrq_start(void) { } +static inline void rcu_sysrq_end(void) { } +#endif /* #else #ifdef CONFIG_RCU_STALL_COMMON */ + +#ifdef CONFIG_NO_HZ_FULL +void rcu_user_enter(void); +void rcu_user_exit(void); +#else +static inline void rcu_user_enter(void) { } +static inline void rcu_user_exit(void) { } +#endif /* CONFIG_NO_HZ_FULL */ + +#ifdef CONFIG_RCU_NOCB_CPU +void rcu_init_nohz(void); +#else /* #ifdef CONFIG_RCU_NOCB_CPU */ +static inline void rcu_init_nohz(void) { } +#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */ + +/** + * RCU_NONIDLE - Indicate idle-loop code that needs RCU readers + * @a: Code that RCU needs to pay attention to. + * + * RCU read-side critical sections are forbidden in the inner idle loop, + * that is, between the rcu_idle_enter() and the rcu_idle_exit() -- RCU + * will happily ignore any such read-side critical sections. However, + * things like powertop need tracepoints in the inner idle loop. + * + * This macro provides the way out: RCU_NONIDLE(do_something_with_RCU()) + * will tell RCU that it needs to pay attention, invoke its argument + * (in this example, calling the do_something_with_RCU() function), + * and then tell RCU to go back to ignoring this CPU. It is permissible + * to nest RCU_NONIDLE() wrappers, but not indefinitely (but the limit is + * on the order of a million or so, even on 32-bit systems). It is + * not legal to block within RCU_NONIDLE(), nor is it permissible to + * transfer control either into or out of RCU_NONIDLE()'s statement. + */ +#define RCU_NONIDLE(a) \ + do { \ + rcu_irq_enter_irqson(); \ + do { a; } while (0); \ + rcu_irq_exit_irqson(); \ + } while (0) + +/* + * Note a quasi-voluntary context switch for RCU-tasks's benefit. + * This is a macro rather than an inline function to avoid #include hell. + */ +#ifdef CONFIG_TASKS_RCU +#define rcu_tasks_qs(t) \ + do { \ + if (READ_ONCE((t)->rcu_tasks_holdout)) \ + WRITE_ONCE((t)->rcu_tasks_holdout, false); \ + } while (0) +#define rcu_note_voluntary_context_switch(t) rcu_tasks_qs(t) +void call_rcu_tasks(struct rcu_head *head, rcu_callback_t func); +void synchronize_rcu_tasks(void); +void exit_tasks_rcu_start(void); +void exit_tasks_rcu_finish(void); +#else /* #ifdef CONFIG_TASKS_RCU */ +#define rcu_tasks_qs(t) do { } while (0) +#define rcu_note_voluntary_context_switch(t) do { } while (0) +#define call_rcu_tasks call_rcu +#define synchronize_rcu_tasks synchronize_rcu +static inline void exit_tasks_rcu_start(void) { } +static inline void exit_tasks_rcu_finish(void) { } +#endif /* #else #ifdef CONFIG_TASKS_RCU */ + +/** + * cond_resched_tasks_rcu_qs - Report potential quiescent states to RCU + * + * This macro resembles cond_resched(), except that it is defined to + * report potential quiescent states to RCU-tasks even if the cond_resched() + * machinery were to be shut off, as some advocate for PREEMPT kernels. + */ +#define cond_resched_tasks_rcu_qs() \ +do { \ + rcu_tasks_qs(current); \ + cond_resched(); \ +} while (0) + +/* + * Infrastructure to implement the synchronize_() primitives in + * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. + */ + +#if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU) +#include +#elif defined(CONFIG_TINY_RCU) +#include +#else +#error "Unknown RCU implementation specified to kernel configuration" +#endif + +/* + * The init_rcu_head_on_stack() and destroy_rcu_head_on_stack() calls + * are needed for dynamic initialization and destruction of rcu_head + * on the stack, and init_rcu_head()/destroy_rcu_head() are needed for + * dynamic initialization and destruction of statically allocated rcu_head + * structures. However, rcu_head structures allocated dynamically in the + * heap don't need any initialization. + */ +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD +void init_rcu_head(struct rcu_head *head); +void destroy_rcu_head(struct rcu_head *head); +void init_rcu_head_on_stack(struct rcu_head *head); +void destroy_rcu_head_on_stack(struct rcu_head *head); +#else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +static inline void init_rcu_head(struct rcu_head *head) { } +static inline void destroy_rcu_head(struct rcu_head *head) { } +static inline void init_rcu_head_on_stack(struct rcu_head *head) { } +static inline void destroy_rcu_head_on_stack(struct rcu_head *head) { } +#endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ + +#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) +bool rcu_lockdep_current_cpu_online(void); +#else /* #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ +static inline bool rcu_lockdep_current_cpu_online(void) { return true; } +#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PROVE_RCU) */ + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +static inline void rcu_lock_acquire(struct lockdep_map *map) +{ + lock_acquire(map, 0, 0, 2, 0, NULL, _THIS_IP_); +} + +static inline void rcu_lock_release(struct lockdep_map *map) +{ + lock_release(map, 1, _THIS_IP_); +} + +extern struct lockdep_map rcu_lock_map; +extern struct lockdep_map rcu_bh_lock_map; +extern struct lockdep_map rcu_sched_lock_map; +extern struct lockdep_map rcu_callback_map; +int debug_lockdep_rcu_enabled(void); +int rcu_read_lock_held(void); +int rcu_read_lock_bh_held(void); +int rcu_read_lock_sched_held(void); +int rcu_read_lock_any_held(void); + +#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +# define rcu_lock_acquire(a) do { } while (0) +# define rcu_lock_release(a) do { } while (0) + +static inline int rcu_read_lock_held(void) +{ + return 1; +} + +static inline int rcu_read_lock_bh_held(void) +{ + return 1; +} + +static inline int rcu_read_lock_sched_held(void) +{ + return !preemptible(); +} + +static inline int rcu_read_lock_any_held(void) +{ + return !preemptible(); +} + +#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +#ifdef CONFIG_PROVE_RCU + +/** + * RCU_LOCKDEP_WARN - emit lockdep splat if specified condition is met + * @c: condition to check + * @s: informative message + */ +#define RCU_LOCKDEP_WARN(c, s) \ + do { \ + static bool __section(.data.unlikely) __warned; \ + if (debug_lockdep_rcu_enabled() && !__warned && (c)) { \ + __warned = true; \ + lockdep_rcu_suspicious(__FILE__, __LINE__, s); \ + } \ + } while (0) + +#if defined(CONFIG_PROVE_RCU) && !defined(CONFIG_PREEMPT_RCU) +static inline void rcu_preempt_sleep_check(void) +{ + RCU_LOCKDEP_WARN(lock_is_held(&rcu_lock_map), + "Illegal context switch in RCU read-side critical section"); +} +#else /* #ifdef CONFIG_PROVE_RCU */ +static inline void rcu_preempt_sleep_check(void) { } +#endif /* #else #ifdef CONFIG_PROVE_RCU */ + +#define rcu_sleep_check() \ + do { \ + rcu_preempt_sleep_check(); \ + RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map), \ + "Illegal context switch in RCU-bh read-side critical section"); \ + RCU_LOCKDEP_WARN(lock_is_held(&rcu_sched_lock_map), \ + "Illegal context switch in RCU-sched read-side critical section"); \ + } while (0) + +#else /* #ifdef CONFIG_PROVE_RCU */ + +#define RCU_LOCKDEP_WARN(c, s) do { } while (0) +#define rcu_sleep_check() do { } while (0) + +#endif /* #else #ifdef CONFIG_PROVE_RCU */ + +/* + * Helper functions for rcu_dereference_check(), rcu_dereference_protected() + * and rcu_assign_pointer(). Some of these could be folded into their + * callers, but they are left separate in order to ease introduction of + * multiple pointers markings to match different RCU implementations + * (e.g., __srcu), should this make sense in the future. + */ + +#ifdef __CHECKER__ +#define rcu_check_sparse(p, space) \ + ((void)(((typeof(*p) space *)p) == p)) +#else /* #ifdef __CHECKER__ */ +#define rcu_check_sparse(p, space) +#endif /* #else #ifdef __CHECKER__ */ + +#define __rcu_access_pointer(p, space) \ +({ \ + typeof(*p) *_________p1 = (typeof(*p) *__force)READ_ONCE(p); \ + rcu_check_sparse(p, space); \ + ((typeof(*p) __force __kernel *)(_________p1)); \ +}) +#define __rcu_dereference_check(p, c, space) \ +({ \ + /* Dependency order vs. p above. */ \ + typeof(*p) *________p1 = (typeof(*p) *__force)READ_ONCE(p); \ + RCU_LOCKDEP_WARN(!(c), "suspicious rcu_dereference_check() usage"); \ + rcu_check_sparse(p, space); \ + ((typeof(*p) __force __kernel *)(________p1)); \ +}) +#define __rcu_dereference_protected(p, c, space) \ +({ \ + RCU_LOCKDEP_WARN(!(c), "suspicious rcu_dereference_protected() usage"); \ + rcu_check_sparse(p, space); \ + ((typeof(*p) __force __kernel *)(p)); \ +}) +#define rcu_dereference_raw(p) \ +({ \ + /* Dependency order vs. p above. */ \ + typeof(p) ________p1 = READ_ONCE(p); \ + ((typeof(*p) __force __kernel *)(________p1)); \ +}) + +/** + * RCU_INITIALIZER() - statically initialize an RCU-protected global variable + * @v: The value to statically initialize with. + */ +#define RCU_INITIALIZER(v) (typeof(*(v)) __force __rcu *)(v) + +/** + * rcu_assign_pointer() - assign to RCU-protected pointer + * @p: pointer to assign to + * @v: value to assign (publish) + * + * Assigns the specified value to the specified RCU-protected + * pointer, ensuring that any concurrent RCU readers will see + * any prior initialization. + * + * Inserts memory barriers on architectures that require them + * (which is most of them), and also prevents the compiler from + * reordering the code that initializes the structure after the pointer + * assignment. More importantly, this call documents which pointers + * will be dereferenced by RCU read-side code. + * + * In some special cases, you may use RCU_INIT_POINTER() instead + * of rcu_assign_pointer(). RCU_INIT_POINTER() is a bit faster due + * to the fact that it does not constrain either the CPU or the compiler. + * That said, using RCU_INIT_POINTER() when you should have used + * rcu_assign_pointer() is a very bad thing that results in + * impossible-to-diagnose memory corruption. So please be careful. + * See the RCU_INIT_POINTER() comment header for details. + * + * Note that rcu_assign_pointer() evaluates each of its arguments only + * once, appearances notwithstanding. One of the "extra" evaluations + * is in typeof() and the other visible only to sparse (__CHECKER__), + * neither of which actually execute the argument. As with most cpp + * macros, this execute-arguments-only-once property is important, so + * please be careful when making changes to rcu_assign_pointer() and the + * other macros that it invokes. + */ +#define rcu_assign_pointer(p, v) \ +do { \ + uintptr_t _r_a_p__v = (uintptr_t)(v); \ + rcu_check_sparse(p, __rcu); \ + \ + if (__builtin_constant_p(v) && (_r_a_p__v) == (uintptr_t)NULL) \ + WRITE_ONCE((p), (typeof(p))(_r_a_p__v)); \ + else \ + smp_store_release(&p, RCU_INITIALIZER((typeof(p))_r_a_p__v)); \ +} while (0) + +/** + * rcu_swap_protected() - swap an RCU and a regular pointer + * @rcu_ptr: RCU pointer + * @ptr: regular pointer + * @c: the conditions under which the dereference will take place + * + * Perform swap(@rcu_ptr, @ptr) where @rcu_ptr is an RCU-annotated pointer and + * @c is the argument that is passed to the rcu_dereference_protected() call + * used to read that pointer. + */ +#define rcu_swap_protected(rcu_ptr, ptr, c) do { \ + typeof(ptr) __tmp = rcu_dereference_protected((rcu_ptr), (c)); \ + rcu_assign_pointer((rcu_ptr), (ptr)); \ + (ptr) = __tmp; \ +} while (0) + +/** + * rcu_access_pointer() - fetch RCU pointer with no dereferencing + * @p: The pointer to read + * + * Return the value of the specified RCU-protected pointer, but omit the + * lockdep checks for being in an RCU read-side critical section. This is + * useful when the value of this pointer is accessed, but the pointer is + * not dereferenced, for example, when testing an RCU-protected pointer + * against NULL. Although rcu_access_pointer() may also be used in cases + * where update-side locks prevent the value of the pointer from changing, + * you should instead use rcu_dereference_protected() for this use case. + * + * It is also permissible to use rcu_access_pointer() when read-side + * access to the pointer was removed at least one grace period ago, as + * is the case in the context of the RCU callback that is freeing up + * the data, or after a synchronize_rcu() returns. This can be useful + * when tearing down multi-linked structures after a grace period + * has elapsed. + */ +#define rcu_access_pointer(p) __rcu_access_pointer((p), __rcu) + +/** + * rcu_dereference_check() - rcu_dereference with debug checking + * @p: The pointer to read, prior to dereferencing + * @c: The conditions under which the dereference will take place + * + * Do an rcu_dereference(), but check that the conditions under which the + * dereference will take place are correct. Typically the conditions + * indicate the various locking conditions that should be held at that + * point. The check should return true if the conditions are satisfied. + * An implicit check for being in an RCU read-side critical section + * (rcu_read_lock()) is included. + * + * For example: + * + * bar = rcu_dereference_check(foo->bar, lockdep_is_held(&foo->lock)); + * + * could be used to indicate to lockdep that foo->bar may only be dereferenced + * if either rcu_read_lock() is held, or that the lock required to replace + * the bar struct at foo->bar is held. + * + * Note that the list of conditions may also include indications of when a lock + * need not be held, for example during initialisation or destruction of the + * target struct: + * + * bar = rcu_dereference_check(foo->bar, lockdep_is_held(&foo->lock) || + * atomic_read(&foo->usage) == 0); + * + * Inserts memory barriers on architectures that require them + * (currently only the Alpha), prevents the compiler from refetching + * (and from merging fetches), and, more importantly, documents exactly + * which pointers are protected by RCU and checks that the pointer is + * annotated as __rcu. + */ +#define rcu_dereference_check(p, c) \ + __rcu_dereference_check((p), (c) || rcu_read_lock_held(), __rcu) + +/** + * rcu_dereference_bh_check() - rcu_dereference_bh with debug checking + * @p: The pointer to read, prior to dereferencing + * @c: The conditions under which the dereference will take place + * + * This is the RCU-bh counterpart to rcu_dereference_check(). + */ +#define rcu_dereference_bh_check(p, c) \ + __rcu_dereference_check((p), (c) || rcu_read_lock_bh_held(), __rcu) + +/** + * rcu_dereference_sched_check() - rcu_dereference_sched with debug checking + * @p: The pointer to read, prior to dereferencing + * @c: The conditions under which the dereference will take place + * + * This is the RCU-sched counterpart to rcu_dereference_check(). + */ +#define rcu_dereference_sched_check(p, c) \ + __rcu_dereference_check((p), (c) || rcu_read_lock_sched_held(), \ + __rcu) + +/* + * The tracing infrastructure traces RCU (we want that), but unfortunately + * some of the RCU checks causes tracing to lock up the system. + * + * The no-tracing version of rcu_dereference_raw() must not call + * rcu_read_lock_held(). + */ +#define rcu_dereference_raw_check(p) __rcu_dereference_check((p), 1, __rcu) + +/** + * rcu_dereference_protected() - fetch RCU pointer when updates prevented + * @p: The pointer to read, prior to dereferencing + * @c: The conditions under which the dereference will take place + * + * Return the value of the specified RCU-protected pointer, but omit + * the READ_ONCE(). This is useful in cases where update-side locks + * prevent the value of the pointer from changing. Please note that this + * primitive does *not* prevent the compiler from repeating this reference + * or combining it with other references, so it should not be used without + * protection of appropriate locks. + * + * This function is only for update-side use. Using this function + * when protected only by rcu_read_lock() will result in infrequent + * but very ugly failures. + */ +#define rcu_dereference_protected(p, c) \ + __rcu_dereference_protected((p), (c), __rcu) + + +/** + * rcu_dereference() - fetch RCU-protected pointer for dereferencing + * @p: The pointer to read, prior to dereferencing + * + * This is a simple wrapper around rcu_dereference_check(). + */ +#define rcu_dereference(p) rcu_dereference_check(p, 0) + +/** + * rcu_dereference_bh() - fetch an RCU-bh-protected pointer for dereferencing + * @p: The pointer to read, prior to dereferencing + * + * Makes rcu_dereference_check() do the dirty work. + */ +#define rcu_dereference_bh(p) rcu_dereference_bh_check(p, 0) + +/** + * rcu_dereference_sched() - fetch RCU-sched-protected pointer for dereferencing + * @p: The pointer to read, prior to dereferencing + * + * Makes rcu_dereference_check() do the dirty work. + */ +#define rcu_dereference_sched(p) rcu_dereference_sched_check(p, 0) + +/** + * rcu_pointer_handoff() - Hand off a pointer from RCU to other mechanism + * @p: The pointer to hand off + * + * This is simply an identity function, but it documents where a pointer + * is handed off from RCU to some other synchronization mechanism, for + * example, reference counting or locking. In C11, it would map to + * kill_dependency(). It could be used as follows:: + * + * rcu_read_lock(); + * p = rcu_dereference(gp); + * long_lived = is_long_lived(p); + * if (long_lived) { + * if (!atomic_inc_not_zero(p->refcnt)) + * long_lived = false; + * else + * p = rcu_pointer_handoff(p); + * } + * rcu_read_unlock(); + */ +#define rcu_pointer_handoff(p) (p) + +/** + * rcu_read_lock() - mark the beginning of an RCU read-side critical section + * + * When synchronize_rcu() is invoked on one CPU while other CPUs + * are within RCU read-side critical sections, then the + * synchronize_rcu() is guaranteed to block until after all the other + * CPUs exit their critical sections. Similarly, if call_rcu() is invoked + * on one CPU while other CPUs are within RCU read-side critical + * sections, invocation of the corresponding RCU callback is deferred + * until after the all the other CPUs exit their critical sections. + * + * Note, however, that RCU callbacks are permitted to run concurrently + * with new RCU read-side critical sections. One way that this can happen + * is via the following sequence of events: (1) CPU 0 enters an RCU + * read-side critical section, (2) CPU 1 invokes call_rcu() to register + * an RCU callback, (3) CPU 0 exits the RCU read-side critical section, + * (4) CPU 2 enters a RCU read-side critical section, (5) the RCU + * callback is invoked. This is legal, because the RCU read-side critical + * section that was running concurrently with the call_rcu() (and which + * therefore might be referencing something that the corresponding RCU + * callback would free up) has completed before the corresponding + * RCU callback is invoked. + * + * RCU read-side critical sections may be nested. Any deferred actions + * will be deferred until the outermost RCU read-side critical section + * completes. + * + * You can avoid reading and understanding the next paragraph by + * following this rule: don't put anything in an rcu_read_lock() RCU + * read-side critical section that would block in a !PREEMPT kernel. + * But if you want the full story, read on! + * + * In non-preemptible RCU implementations (TREE_RCU and TINY_RCU), + * it is illegal to block while in an RCU read-side critical section. + * In preemptible RCU implementations (PREEMPT_RCU) in CONFIG_PREEMPTION + * kernel builds, RCU read-side critical sections may be preempted, + * but explicit blocking is illegal. Finally, in preemptible RCU + * implementations in real-time (with -rt patchset) kernel builds, RCU + * read-side critical sections may be preempted and they may also block, but + * only when acquiring spinlocks that are subject to priority inheritance. + */ +static __always_inline void rcu_read_lock(void) +{ + __rcu_read_lock(); + __acquire(RCU); + rcu_lock_acquire(&rcu_lock_map); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_lock() used illegally while idle"); +} + +/* + * So where is rcu_write_lock()? It does not exist, as there is no + * way for writers to lock out RCU readers. This is a feature, not + * a bug -- this property is what provides RCU's performance benefits. + * Of course, writers must coordinate with each other. The normal + * spinlock primitives work well for this, but any other technique may be + * used as well. RCU does not care how the writers keep out of each + * others' way, as long as they do so. + */ + +/** + * rcu_read_unlock() - marks the end of an RCU read-side critical section. + * + * In most situations, rcu_read_unlock() is immune from deadlock. + * However, in kernels built with CONFIG_RCU_BOOST, rcu_read_unlock() + * is responsible for deboosting, which it does via rt_mutex_unlock(). + * Unfortunately, this function acquires the scheduler's runqueue and + * priority-inheritance spinlocks. This means that deadlock could result + * if the caller of rcu_read_unlock() already holds one of these locks or + * any lock that is ever acquired while holding them. + * + * That said, RCU readers are never priority boosted unless they were + * preempted. Therefore, one way to avoid deadlock is to make sure + * that preemption never happens within any RCU read-side critical + * section whose outermost rcu_read_unlock() is called with one of + * rt_mutex_unlock()'s locks held. Such preemption can be avoided in + * a number of ways, for example, by invoking preempt_disable() before + * critical section's outermost rcu_read_lock(). + * + * Given that the set of locks acquired by rt_mutex_unlock() might change + * at any time, a somewhat more future-proofed approach is to make sure + * that that preemption never happens within any RCU read-side critical + * section whose outermost rcu_read_unlock() is called with irqs disabled. + * This approach relies on the fact that rt_mutex_unlock() currently only + * acquires irq-disabled locks. + * + * The second of these two approaches is best in most situations, + * however, the first approach can also be useful, at least to those + * developers willing to keep abreast of the set of locks acquired by + * rt_mutex_unlock(). + * + * See rcu_read_lock() for more information. + */ +static inline void rcu_read_unlock(void) +{ + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_unlock() used illegally while idle"); + __release(RCU); + __rcu_read_unlock(); + rcu_lock_release(&rcu_lock_map); /* Keep acq info for rls diags. */ +} + +/** + * rcu_read_lock_bh() - mark the beginning of an RCU-bh critical section + * + * This is equivalent of rcu_read_lock(), but also disables softirqs. + * Note that anything else that disables softirqs can also serve as + * an RCU read-side critical section. + * + * Note that rcu_read_lock_bh() and the matching rcu_read_unlock_bh() + * must occur in the same context, for example, it is illegal to invoke + * rcu_read_unlock_bh() from one task if the matching rcu_read_lock_bh() + * was invoked from some other task. + */ +static inline void rcu_read_lock_bh(void) +{ + local_bh_disable(); + __acquire(RCU_BH); + rcu_lock_acquire(&rcu_bh_lock_map); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_lock_bh() used illegally while idle"); +} + +/* + * rcu_read_unlock_bh - marks the end of a softirq-only RCU critical section + * + * See rcu_read_lock_bh() for more information. + */ +static inline void rcu_read_unlock_bh(void) +{ + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_unlock_bh() used illegally while idle"); + rcu_lock_release(&rcu_bh_lock_map); + __release(RCU_BH); + local_bh_enable(); +} + +/** + * rcu_read_lock_sched() - mark the beginning of a RCU-sched critical section + * + * This is equivalent of rcu_read_lock(), but disables preemption. + * Read-side critical sections can also be introduced by anything else + * that disables preemption, including local_irq_disable() and friends. + * + * Note that rcu_read_lock_sched() and the matching rcu_read_unlock_sched() + * must occur in the same context, for example, it is illegal to invoke + * rcu_read_unlock_sched() from process context if the matching + * rcu_read_lock_sched() was invoked from an NMI handler. + */ +static inline void rcu_read_lock_sched(void) +{ + preempt_disable(); + __acquire(RCU_SCHED); + rcu_lock_acquire(&rcu_sched_lock_map); + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_lock_sched() used illegally while idle"); +} + +/* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ +static inline notrace void rcu_read_lock_sched_notrace(void) +{ + preempt_disable_notrace(); + __acquire(RCU_SCHED); +} + +/* + * rcu_read_unlock_sched - marks the end of a RCU-classic critical section + * + * See rcu_read_lock_sched for more information. + */ +static inline void rcu_read_unlock_sched(void) +{ + RCU_LOCKDEP_WARN(!rcu_is_watching(), + "rcu_read_unlock_sched() used illegally while idle"); + rcu_lock_release(&rcu_sched_lock_map); + __release(RCU_SCHED); + preempt_enable(); +} + +/* Used by lockdep and tracing: cannot be traced, cannot call lockdep. */ +static inline notrace void rcu_read_unlock_sched_notrace(void) +{ + __release(RCU_SCHED); + preempt_enable_notrace(); +} + +/** + * RCU_INIT_POINTER() - initialize an RCU protected pointer + * @p: The pointer to be initialized. + * @v: The value to initialized the pointer to. + * + * Initialize an RCU-protected pointer in special cases where readers + * do not need ordering constraints on the CPU or the compiler. These + * special cases are: + * + * 1. This use of RCU_INIT_POINTER() is NULLing out the pointer *or* + * 2. The caller has taken whatever steps are required to prevent + * RCU readers from concurrently accessing this pointer *or* + * 3. The referenced data structure has already been exposed to + * readers either at compile time or via rcu_assign_pointer() *and* + * + * a. You have not made *any* reader-visible changes to + * this structure since then *or* + * b. It is OK for readers accessing this structure from its + * new location to see the old state of the structure. (For + * example, the changes were to statistical counters or to + * other state where exact synchronization is not required.) + * + * Failure to follow these rules governing use of RCU_INIT_POINTER() will + * result in impossible-to-diagnose memory corruption. As in the structures + * will look OK in crash dumps, but any concurrent RCU readers might + * see pre-initialized values of the referenced data structure. So + * please be very careful how you use RCU_INIT_POINTER()!!! + * + * If you are creating an RCU-protected linked structure that is accessed + * by a single external-to-structure RCU-protected pointer, then you may + * use RCU_INIT_POINTER() to initialize the internal RCU-protected + * pointers, but you must use rcu_assign_pointer() to initialize the + * external-to-structure pointer *after* you have completely initialized + * the reader-accessible portions of the linked structure. + * + * Note that unlike rcu_assign_pointer(), RCU_INIT_POINTER() provides no + * ordering guarantees for either the CPU or the compiler. + */ +#define RCU_INIT_POINTER(p, v) \ + do { \ + rcu_check_sparse(p, __rcu); \ + WRITE_ONCE(p, RCU_INITIALIZER(v)); \ + } while (0) + +/** + * RCU_POINTER_INITIALIZER() - statically initialize an RCU protected pointer + * @p: The pointer to be initialized. + * @v: The value to initialized the pointer to. + * + * GCC-style initialization for an RCU-protected pointer in a structure field. + */ +#define RCU_POINTER_INITIALIZER(p, v) \ + .p = RCU_INITIALIZER(v) + +/* + * Does the specified offset indicate that the corresponding rcu_head + * structure can be handled by kfree_rcu()? + */ +#define __is_kfree_rcu_offset(offset) ((offset) < 4096) + +/* + * Helper macro for kfree_rcu() to prevent argument-expansion eyestrain. + */ +#define __kfree_rcu(head, offset) \ + do { \ + BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); \ + kfree_call_rcu(head, (rcu_callback_t)(unsigned long)(offset)); \ + } while (0) + +/** + * kfree_rcu() - kfree an object after a grace period. + * @ptr: pointer to kfree + * @rhf: the name of the struct rcu_head within the type of @ptr. + * + * Many rcu callbacks functions just call kfree() on the base structure. + * These functions are trivial, but their size adds up, and furthermore + * when they are used in a kernel module, that module must invoke the + * high-latency rcu_barrier() function at module-unload time. + * + * The kfree_rcu() function handles this issue. Rather than encoding a + * function address in the embedded rcu_head structure, kfree_rcu() instead + * encodes the offset of the rcu_head structure within the base structure. + * Because the functions are not allowed in the low-order 4096 bytes of + * kernel virtual memory, offsets up to 4095 bytes can be accommodated. + * If the offset is larger than 4095 bytes, a compile-time error will + * be generated in __kfree_rcu(). If this error is triggered, you can + * either fall back to use of call_rcu() or rearrange the structure to + * position the rcu_head structure into the first 4096 bytes. + * + * Note that the allowable offset might decrease in the future, for example, + * to allow something like kmem_cache_free_rcu(). + * + * The BUILD_BUG_ON check must not involve any function calls, hence the + * checks are done in macros here. + */ +#define kfree_rcu(ptr, rhf) \ +do { \ + typeof (ptr) ___p = (ptr); \ + \ + if (___p) \ + __kfree_rcu(&((___p)->rhf), offsetof(typeof(*(ptr)), rhf)); \ +} while (0) + +/* + * Place this after a lock-acquisition primitive to guarantee that + * an UNLOCK+LOCK pair acts as a full barrier. This guarantee applies + * if the UNLOCK and LOCK are executed by the same CPU or if the + * UNLOCK and LOCK operate on the same lock variable. + */ +#ifdef CONFIG_ARCH_WEAK_RELEASE_ACQUIRE +#define smp_mb__after_unlock_lock() smp_mb() /* Full ordering for lock. */ +#else /* #ifdef CONFIG_ARCH_WEAK_RELEASE_ACQUIRE */ +#define smp_mb__after_unlock_lock() do { } while (0) +#endif /* #else #ifdef CONFIG_ARCH_WEAK_RELEASE_ACQUIRE */ + + +/* Has the specified rcu_head structure been handed to call_rcu()? */ + +/** + * rcu_head_init - Initialize rcu_head for rcu_head_after_call_rcu() + * @rhp: The rcu_head structure to initialize. + * + * If you intend to invoke rcu_head_after_call_rcu() to test whether a + * given rcu_head structure has already been passed to call_rcu(), then + * you must also invoke this rcu_head_init() function on it just after + * allocating that structure. Calls to this function must not race with + * calls to call_rcu(), rcu_head_after_call_rcu(), or callback invocation. + */ +static inline void rcu_head_init(struct rcu_head *rhp) +{ + rhp->func = (rcu_callback_t)~0L; +} + +/** + * rcu_head_after_call_rcu - Has this rcu_head been passed to call_rcu()? + * @rhp: The rcu_head structure to test. + * @f: The function passed to call_rcu() along with @rhp. + * + * Returns @true if the @rhp has been passed to call_rcu() with @func, + * and @false otherwise. Emits a warning in any other case, including + * the case where @rhp has already been invoked after a grace period. + * Calls to this function must not race with callback invocation. One way + * to avoid such races is to enclose the call to rcu_head_after_call_rcu() + * in an RCU read-side critical section that includes a read-side fetch + * of the pointer to the structure containing @rhp. + */ +static inline bool +rcu_head_after_call_rcu(struct rcu_head *rhp, rcu_callback_t f) +{ + rcu_callback_t func = READ_ONCE(rhp->func); + + if (func == f) + return true; + WARN_ON_ONCE(func != (rcu_callback_t)~0L); + return false; +} + +#endif /* __LINUX_RCUPDATE_H */ diff --git a/t/tree/include/linux/rcutree.h b/t/tree/include/linux/rcutree.h new file mode 100644 index 00000000..18b1ed98 --- /dev/null +++ b/t/tree/include/linux/rcutree.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Read-Copy Update mechanism for mutual exclusion (tree-based version) + * + * Copyright IBM Corporation, 2008 + * + * Author: Dipankar Sarma + * Paul E. McKenney Hierarchical algorithm + * + * Based on the original work by Paul McKenney + * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU + */ + +#ifndef __LINUX_RCUTREE_H +#define __LINUX_RCUTREE_H + +void rcu_softirq_qs(void); +void rcu_note_context_switch(bool preempt); +int rcu_needs_cpu(u64 basem, u64 *nextevt); +void rcu_cpu_stall_reset(void); + +/* + * Note a virtualization-based context switch. This is simply a + * wrapper around rcu_note_context_switch(), which allows TINY_RCU + * to save a few bytes. The caller must have disabled interrupts. + */ +static inline void rcu_virt_note_context_switch(int cpu) +{ + rcu_note_context_switch(false); +} + +void synchronize_rcu_expedited(void); +void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func); + +void rcu_barrier(void); +bool rcu_eqs_special_set(int cpu); +unsigned long get_state_synchronize_rcu(void); +void cond_synchronize_rcu(unsigned long oldstate); + +void rcu_idle_enter(void); +void rcu_idle_exit(void); +void rcu_irq_enter(void); +void rcu_irq_exit(void); +void rcu_irq_enter_irqson(void); +void rcu_irq_exit_irqson(void); + +void exit_rcu(void); + +void rcu_scheduler_starting(void); +extern int rcu_scheduler_active __read_mostly; +void rcu_end_inkernel_boot(void); +bool rcu_is_watching(void); +#ifndef CONFIG_PREEMPTION +void rcu_all_qs(void); +#endif + +/* RCUtree hotplug events */ +int rcutree_prepare_cpu(unsigned int cpu); +int rcutree_online_cpu(unsigned int cpu); +int rcutree_offline_cpu(unsigned int cpu); +int rcutree_dead_cpu(unsigned int cpu); +int rcutree_dying_cpu(unsigned int cpu); +void rcu_cpu_starting(unsigned int cpu); + +#endif /* __LINUX_RCUTREE_H */ diff --git a/t/tree/include/linux/srcu.h b/t/tree/include/linux/srcu.h new file mode 100644 index 00000000..e432cc92 --- /dev/null +++ b/t/tree/include/linux/srcu.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sleepable Read-Copy Update mechanism for mutual exclusion + * + * Copyright (C) IBM Corporation, 2006 + * Copyright (C) Fujitsu, 2012 + * + * Author: Paul McKenney + * Lai Jiangshan + * + * For detailed explanation of Read-Copy Update mechanism see - + * Documentation/RCU/ *.txt + * + */ + +#ifndef _LINUX_SRCU_H +#define _LINUX_SRCU_H + +#include +#include +#include +#include + +struct srcu_struct; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +int __init_srcu_struct(struct srcu_struct *ssp, const char *name, + struct lock_class_key *key); + +#define init_srcu_struct(ssp) \ +({ \ + static struct lock_class_key __srcu_key; \ + \ + __init_srcu_struct((ssp), #ssp, &__srcu_key); \ +}) + +#define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name }, +#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +int init_srcu_struct(struct srcu_struct *ssp); + +#define __SRCU_DEP_MAP_INIT(srcu_name) +#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +#ifdef CONFIG_TINY_SRCU +#include +#elif defined(CONFIG_TREE_SRCU) +#include +#elif defined(CONFIG_SRCU) +#error "Unknown SRCU implementation specified to kernel configuration" +#else +/* Dummy definition for things like notifiers. Actual use gets link error. */ +struct srcu_struct { }; +#endif + +void call_srcu(struct srcu_struct *ssp, struct rcu_head *head, + void (*func)(struct rcu_head *head)); +void cleanup_srcu_struct(struct srcu_struct *ssp); +int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp); +void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp); +void synchronize_srcu(struct srcu_struct *ssp); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +/** + * srcu_read_lock_held - might we be in SRCU read-side critical section? + * @ssp: The srcu_struct structure to check + * + * If CONFIG_DEBUG_LOCK_ALLOC is selected, returns nonzero iff in an SRCU + * read-side critical section. In absence of CONFIG_DEBUG_LOCK_ALLOC, + * this assumes we are in an SRCU read-side critical section unless it can + * prove otherwise. + * + * Checks debug_lockdep_rcu_enabled() to prevent false positives during boot + * and while lockdep is disabled. + * + * Note that SRCU is based on its own statemachine and it doesn't + * relies on normal RCU, it can be called from the CPU which + * is in the idle loop from an RCU point of view or offline. + */ +static inline int srcu_read_lock_held(const struct srcu_struct *ssp) +{ + if (!debug_lockdep_rcu_enabled()) + return 1; + return lock_is_held(&ssp->dep_map); +} + +#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +static inline int srcu_read_lock_held(const struct srcu_struct *ssp) +{ + return 1; +} + +#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +/** + * srcu_dereference_check - fetch SRCU-protected pointer for later dereferencing + * @p: the pointer to fetch and protect for later dereferencing + * @ssp: pointer to the srcu_struct, which is used to check that we + * really are in an SRCU read-side critical section. + * @c: condition to check for update-side use + * + * If PROVE_RCU is enabled, invoking this outside of an RCU read-side + * critical section will result in an RCU-lockdep splat, unless @c evaluates + * to 1. The @c argument will normally be a logical expression containing + * lockdep_is_held() calls. + */ +#define srcu_dereference_check(p, ssp, c) \ + __rcu_dereference_check((p), (c) || srcu_read_lock_held(ssp), __rcu) + +/** + * srcu_dereference - fetch SRCU-protected pointer for later dereferencing + * @p: the pointer to fetch and protect for later dereferencing + * @ssp: pointer to the srcu_struct, which is used to check that we + * really are in an SRCU read-side critical section. + * + * Makes rcu_dereference_check() do the dirty work. If PROVE_RCU + * is enabled, invoking this outside of an RCU read-side critical + * section will result in an RCU-lockdep splat. + */ +#define srcu_dereference(p, ssp) srcu_dereference_check((p), (ssp), 0) + +/** + * srcu_dereference_notrace - no tracing and no lockdep calls from here + * @p: the pointer to fetch and protect for later dereferencing + * @ssp: pointer to the srcu_struct, which is used to check that we + * really are in an SRCU read-side critical section. + */ +#define srcu_dereference_notrace(p, ssp) srcu_dereference_check((p), (ssp), 1) + +/** + * srcu_read_lock - register a new reader for an SRCU-protected structure. + * @ssp: srcu_struct in which to register the new reader. + * + * Enter an SRCU read-side critical section. Note that SRCU read-side + * critical sections may be nested. However, it is illegal to + * call anything that waits on an SRCU grace period for the same + * srcu_struct, whether directly or indirectly. Please note that + * one way to indirectly wait on an SRCU grace period is to acquire + * a mutex that is held elsewhere while calling synchronize_srcu() or + * synchronize_srcu_expedited(). + * + * Note that srcu_read_lock() and the matching srcu_read_unlock() must + * occur in the same context, for example, it is illegal to invoke + * srcu_read_unlock() in an irq handler if the matching srcu_read_lock() + * was invoked in process context. + */ +static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp) +{ + int retval; + + retval = __srcu_read_lock(ssp); + rcu_lock_acquire(&(ssp)->dep_map); + return retval; +} + +/* Used by tracing, cannot be traced and cannot invoke lockdep. */ +static inline notrace int +srcu_read_lock_notrace(struct srcu_struct *ssp) __acquires(ssp) +{ + int retval; + + retval = __srcu_read_lock(ssp); + return retval; +} + +/** + * srcu_read_unlock - unregister a old reader from an SRCU-protected structure. + * @ssp: srcu_struct in which to unregister the old reader. + * @idx: return value from corresponding srcu_read_lock(). + * + * Exit an SRCU read-side critical section. + */ +static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx) + __releases(ssp) +{ + WARN_ON_ONCE(idx & ~0x1); + rcu_lock_release(&(ssp)->dep_map); + __srcu_read_unlock(ssp, idx); +} + +/* Used by tracing, cannot be traced and cannot call lockdep. */ +static inline notrace void +srcu_read_unlock_notrace(struct srcu_struct *ssp, int idx) __releases(ssp) +{ + __srcu_read_unlock(ssp, idx); +} + +/** + * smp_mb__after_srcu_read_unlock - ensure full ordering after srcu_read_unlock + * + * Converts the preceding srcu_read_unlock into a two-way memory barrier. + * + * Call this after srcu_read_unlock, to guarantee that all memory operations + * that occur after smp_mb__after_srcu_read_unlock will appear to happen after + * the preceding srcu_read_unlock. + */ +static inline void smp_mb__after_srcu_read_unlock(void) +{ + /* __srcu_read_unlock has smp_mb() internally so nothing to do here. */ +} + +#endif diff --git a/t/tree/include/linux/srcutree.h b/t/tree/include/linux/srcutree.h new file mode 100644 index 00000000..9cfcc8a7 --- /dev/null +++ b/t/tree/include/linux/srcutree.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sleepable Read-Copy Update mechanism for mutual exclusion, + * tree variant. + * + * Copyright (C) IBM Corporation, 2017 + * + * Author: Paul McKenney + */ + +#ifndef _LINUX_SRCU_TREE_H +#define _LINUX_SRCU_TREE_H + +#include +#include + +struct srcu_node; +struct srcu_struct; + +/* + * Per-CPU structure feeding into leaf srcu_node, similar in function + * to rcu_node. + */ +struct srcu_data { + /* Read-side state. */ + unsigned long srcu_lock_count[2]; /* Locks per CPU. */ + unsigned long srcu_unlock_count[2]; /* Unlocks per CPU. */ + + /* Update-side state. */ + spinlock_t __private lock ____cacheline_internodealigned_in_smp; + struct rcu_segcblist srcu_cblist; /* List of callbacks.*/ + unsigned long srcu_gp_seq_needed; /* Furthest future GP needed. */ + unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */ + bool srcu_cblist_invoking; /* Invoking these CBs? */ + struct timer_list delay_work; /* Delay for CB invoking */ + struct work_struct work; /* Context for CB invoking. */ + struct rcu_head srcu_barrier_head; /* For srcu_barrier() use. */ + struct srcu_node *mynode; /* Leaf srcu_node. */ + unsigned long grpmask; /* Mask for leaf srcu_node */ + /* ->srcu_data_have_cbs[]. */ + int cpu; + struct srcu_struct *ssp; +}; + +/* + * Node in SRCU combining tree, similar in function to rcu_data. + */ +struct srcu_node { + spinlock_t __private lock; + unsigned long srcu_have_cbs[4]; /* GP seq for children */ + /* having CBs, but only */ + /* is > ->srcu_gq_seq. */ + unsigned long srcu_data_have_cbs[4]; /* Which srcu_data structs */ + /* have CBs for given GP? */ + unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */ + struct srcu_node *srcu_parent; /* Next up in tree. */ + int grplo; /* Least CPU for node. */ + int grphi; /* Biggest CPU for node. */ +}; + +/* + * Per-SRCU-domain structure, similar in function to rcu_state. + */ +struct srcu_struct { + struct srcu_node node[NUM_RCU_NODES]; /* Combining tree. */ + struct srcu_node *level[RCU_NUM_LVLS + 1]; + /* First node at each level. */ + struct mutex srcu_cb_mutex; /* Serialize CB preparation. */ + spinlock_t __private lock; /* Protect counters */ + struct mutex srcu_gp_mutex; /* Serialize GP work. */ + unsigned int srcu_idx; /* Current rdr array element. */ + unsigned long srcu_gp_seq; /* Grace-period seq #. */ + unsigned long srcu_gp_seq_needed; /* Latest gp_seq needed. */ + unsigned long srcu_gp_seq_needed_exp; /* Furthest future exp GP. */ + unsigned long srcu_last_gp_end; /* Last GP end timestamp (ns) */ + struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */ + unsigned long srcu_barrier_seq; /* srcu_barrier seq #. */ + struct mutex srcu_barrier_mutex; /* Serialize barrier ops. */ + struct completion srcu_barrier_completion; + /* Awaken barrier rq at end. */ + atomic_t srcu_barrier_cpu_cnt; /* # CPUs not yet posting a */ + /* callback for the barrier */ + /* operation. */ + struct delayed_work work; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ +}; + +/* Values for state variable (bottom bits of ->srcu_gp_seq). */ +#define SRCU_STATE_IDLE 0 +#define SRCU_STATE_SCAN1 1 +#define SRCU_STATE_SCAN2 2 + +#define __SRCU_STRUCT_INIT(name, pcpu_name) \ +{ \ + .sda = &pcpu_name, \ + .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ + .srcu_gp_seq_needed = -1UL, \ + .work = __DELAYED_WORK_INITIALIZER(name.work, NULL, 0), \ + __SRCU_DEP_MAP_INIT(name) \ +} + +/* + * Define and initialize a srcu struct at build time. + * Do -not- call init_srcu_struct() nor cleanup_srcu_struct() on it. + * + * Note that although DEFINE_STATIC_SRCU() hides the name from other + * files, the per-CPU variable rules nevertheless require that the + * chosen name be globally unique. These rules also prohibit use of + * DEFINE_STATIC_SRCU() within a function. If these rules are too + * restrictive, declare the srcu_struct manually. For example, in + * each file: + * + * static struct srcu_struct my_srcu; + * + * Then, before the first use of each my_srcu, manually initialize it: + * + * init_srcu_struct(&my_srcu); + * + * See include/linux/percpu-defs.h for the rules on per-CPU variables. + */ +#ifdef MODULE +# define __DEFINE_SRCU(name, is_static) \ + is_static struct srcu_struct name; \ + struct srcu_struct * const __srcu_struct_##name \ + __section("___srcu_struct_ptrs") = &name +#else +# define __DEFINE_SRCU(name, is_static) \ + static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data); \ + is_static struct srcu_struct name = \ + __SRCU_STRUCT_INIT(name, name##_srcu_data) +#endif +#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */) +#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static) + +void synchronize_srcu_expedited(struct srcu_struct *ssp); +void srcu_barrier(struct srcu_struct *ssp); +void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf); + +#endif diff --git a/t/tree/include/linux/stackdepot.h b/t/tree/include/linux/stackdepot.h new file mode 100644 index 00000000..3efa97d4 --- /dev/null +++ b/t/tree/include/linux/stackdepot.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * A generic stack depot implementation + * + * Author: Alexander Potapenko + * Copyright (C) 2016 Google, Inc. + * + * Based on code by Dmitry Chernenkov. + */ + +#ifndef _LINUX_STACKDEPOT_H +#define _LINUX_STACKDEPOT_H + +typedef u32 depot_stack_handle_t; + +depot_stack_handle_t stack_depot_save(unsigned long *entries, + unsigned int nr_entries, gfp_t gfp_flags); + +unsigned int stack_depot_fetch(depot_stack_handle_t handle, + unsigned long **entries); + +#endif diff --git a/t/tree/include/linux/uprobes.h b/t/tree/include/linux/uprobes.h new file mode 100644 index 00000000..f46e0ca0 --- /dev/null +++ b/t/tree/include/linux/uprobes.h @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_UPROBES_H +#define _LINUX_UPROBES_H +/* + * User-space Probes (UProbes) + * + * Copyright (C) IBM Corporation, 2008-2012 + * Authors: + * Srikar Dronamraju + * Jim Keniston + * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra + */ + +#include +#include +#include +#include + +struct vm_area_struct; +struct mm_struct; +struct inode; +struct notifier_block; +struct page; + +#define UPROBE_HANDLER_REMOVE 1 +#define UPROBE_HANDLER_MASK 1 + +#define MAX_URETPROBE_DEPTH 64 + +enum uprobe_filter_ctx { + UPROBE_FILTER_REGISTER, + UPROBE_FILTER_UNREGISTER, + UPROBE_FILTER_MMAP, +}; + +struct uprobe_consumer { + int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); + int (*ret_handler)(struct uprobe_consumer *self, + unsigned long func, + struct pt_regs *regs); + bool (*filter)(struct uprobe_consumer *self, + enum uprobe_filter_ctx ctx, + struct mm_struct *mm); + + struct uprobe_consumer *next; +}; + +#ifdef CONFIG_UPROBES +#include + +enum uprobe_task_state { + UTASK_RUNNING, + UTASK_SSTEP, + UTASK_SSTEP_ACK, + UTASK_SSTEP_TRAPPED, +}; + +/* + * uprobe_task: Metadata of a task while it singlesteps. + */ +struct uprobe_task { + enum uprobe_task_state state; + + union { + struct { + struct arch_uprobe_task autask; + unsigned long vaddr; + }; + + struct { + struct callback_head dup_xol_work; + unsigned long dup_xol_addr; + }; + }; + + struct uprobe *active_uprobe; + unsigned long xol_vaddr; + + struct return_instance *return_instances; + unsigned int depth; +}; + +struct return_instance { + struct uprobe *uprobe; + unsigned long func; + unsigned long stack; /* stack pointer */ + unsigned long orig_ret_vaddr; /* original return address */ + bool chained; /* true, if instance is nested */ + + struct return_instance *next; /* keep as stack */ +}; + +enum rp_check { + RP_CHECK_CALL, + RP_CHECK_CHAIN_CALL, + RP_CHECK_RET, +}; + +struct xol_area; + +struct uprobes_state { + struct xol_area *xol_area; +}; + +extern void __init uprobes_init(void); +extern int set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); +extern int set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); +extern bool is_swbp_insn(uprobe_opcode_t *insn); +extern bool is_trap_insn(uprobe_opcode_t *insn); +extern unsigned long uprobe_get_swbp_addr(struct pt_regs *regs); +extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs); +extern int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); +extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); +extern int uprobe_register_refctr(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc); +extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); +extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); +extern int uprobe_mmap(struct vm_area_struct *vma); +extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); +extern void uprobe_start_dup_mmap(void); +extern void uprobe_end_dup_mmap(void); +extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); +extern void uprobe_free_utask(struct task_struct *t); +extern void uprobe_copy_process(struct task_struct *t, unsigned long flags); +extern int uprobe_post_sstep_notifier(struct pt_regs *regs); +extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); +extern void uprobe_notify_resume(struct pt_regs *regs); +extern bool uprobe_deny_signal(void); +extern bool arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); +extern void uprobe_clear_state(struct mm_struct *mm); +extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr); +extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); +extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); +extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); +extern bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx, struct pt_regs *regs); +extern bool arch_uprobe_ignore(struct arch_uprobe *aup, struct pt_regs *regs); +extern void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + void *src, unsigned long len); +#else /* !CONFIG_UPROBES */ +struct uprobes_state { +}; + +static inline void uprobes_init(void) +{ +} + +#define uprobe_get_trap_addr(regs) instruction_pointer(regs) + +static inline int +uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) +{ + return -ENOSYS; +} +static inline int uprobe_register_refctr(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc) +{ + return -ENOSYS; +} +static inline int +uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool add) +{ + return -ENOSYS; +} +static inline void +uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) +{ +} +static inline int uprobe_mmap(struct vm_area_struct *vma) +{ + return 0; +} +static inline void +uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) +{ +} +static inline void uprobe_start_dup_mmap(void) +{ +} +static inline void uprobe_end_dup_mmap(void) +{ +} +static inline void +uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) +{ +} +static inline void uprobe_notify_resume(struct pt_regs *regs) +{ +} +static inline bool uprobe_deny_signal(void) +{ + return false; +} +static inline void uprobe_free_utask(struct task_struct *t) +{ +} +static inline void uprobe_copy_process(struct task_struct *t, unsigned long flags) +{ +} +static inline void uprobe_clear_state(struct mm_struct *mm) +{ +} +#endif /* !CONFIG_UPROBES */ +#endif /* _LINUX_UPROBES_H */ diff --git a/t/tree/include/linux/xarray.h b/t/tree/include/linux/xarray.h new file mode 100644 index 00000000..86eecbd9 --- /dev/null +++ b/t/tree/include/linux/xarray.h @@ -0,0 +1,1750 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef _LINUX_XARRAY_H +#define _LINUX_XARRAY_H +/* + * eXtensible Arrays + * Copyright (c) 2017 Microsoft Corporation + * Author: Matthew Wilcox + * + * See Documentation/core-api/xarray.rst for how to use the XArray. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The bottom two bits of the entry determine how the XArray interprets + * the contents: + * + * 00: Pointer entry + * 10: Internal entry + * x1: Value entry or tagged pointer + * + * Attempting to store internal entries in the XArray is a bug. + * + * Most internal entries are pointers to the next node in the tree. + * The following internal entries have a special meaning: + * + * 0-62: Sibling entries + * 256: Zero entry + * 257: Retry entry + * + * Errors are also represented as internal entries, but use the negative + * space (-4094 to -2). They're never stored in the slots array; only + * returned by the normal API. + */ + +#define BITS_PER_XA_VALUE (BITS_PER_LONG - 1) + +/** + * xa_mk_value() - Create an XArray entry from an integer. + * @v: Value to store in XArray. + * + * Context: Any context. + * Return: An entry suitable for storing in the XArray. + */ +static inline void *xa_mk_value(unsigned long v) +{ + WARN_ON((long)v < 0); + return (void *)((v << 1) | 1); +} + +/** + * xa_to_value() - Get value stored in an XArray entry. + * @entry: XArray entry. + * + * Context: Any context. + * Return: The value stored in the XArray entry. + */ +static inline unsigned long xa_to_value(const void *entry) +{ + return (unsigned long)entry >> 1; +} + +/** + * xa_is_value() - Determine if an entry is a value. + * @entry: XArray entry. + * + * Context: Any context. + * Return: True if the entry is a value, false if it is a pointer. + */ +static inline bool xa_is_value(const void *entry) +{ + return (unsigned long)entry & 1; +} + +/** + * xa_tag_pointer() - Create an XArray entry for a tagged pointer. + * @p: Plain pointer. + * @tag: Tag value (0, 1 or 3). + * + * If the user of the XArray prefers, they can tag their pointers instead + * of storing value entries. Three tags are available (0, 1 and 3). + * These are distinct from the xa_mark_t as they are not replicated up + * through the array and cannot be searched for. + * + * Context: Any context. + * Return: An XArray entry. + */ +static inline void *xa_tag_pointer(void *p, unsigned long tag) +{ + return (void *)((unsigned long)p | tag); +} + +/** + * xa_untag_pointer() - Turn an XArray entry into a plain pointer. + * @entry: XArray entry. + * + * If you have stored a tagged pointer in the XArray, call this function + * to get the untagged version of the pointer. + * + * Context: Any context. + * Return: A pointer. + */ +static inline void *xa_untag_pointer(void *entry) +{ + return (void *)((unsigned long)entry & ~3UL); +} + +/** + * xa_pointer_tag() - Get the tag stored in an XArray entry. + * @entry: XArray entry. + * + * If you have stored a tagged pointer in the XArray, call this function + * to get the tag of that pointer. + * + * Context: Any context. + * Return: A tag. + */ +static inline unsigned int xa_pointer_tag(void *entry) +{ + return (unsigned long)entry & 3UL; +} + +/* + * xa_mk_internal() - Create an internal entry. + * @v: Value to turn into an internal entry. + * + * Internal entries are used for a number of purposes. Entries 0-255 are + * used for sibling entries (only 0-62 are used by the current code). 256 + * is used for the retry entry. 257 is used for the reserved / zero entry. + * Negative internal entries are used to represent errnos. Node pointers + * are also tagged as internal entries in some situations. + * + * Context: Any context. + * Return: An XArray internal entry corresponding to this value. + */ +static inline void *xa_mk_internal(unsigned long v) +{ + return (void *)((v << 2) | 2); +} + +/* + * xa_to_internal() - Extract the value from an internal entry. + * @entry: XArray entry. + * + * Context: Any context. + * Return: The value which was stored in the internal entry. + */ +static inline unsigned long xa_to_internal(const void *entry) +{ + return (unsigned long)entry >> 2; +} + +/* + * xa_is_internal() - Is the entry an internal entry? + * @entry: XArray entry. + * + * Context: Any context. + * Return: %true if the entry is an internal entry. + */ +static inline bool xa_is_internal(const void *entry) +{ + return ((unsigned long)entry & 3) == 2; +} + +#define XA_ZERO_ENTRY xa_mk_internal(257) + +/** + * xa_is_zero() - Is the entry a zero entry? + * @entry: Entry retrieved from the XArray + * + * The normal API will return NULL as the contents of a slot containing + * a zero entry. You can only see zero entries by using the advanced API. + * + * Return: %true if the entry is a zero entry. + */ +static inline bool xa_is_zero(const void *entry) +{ + return unlikely(entry == XA_ZERO_ENTRY); +} + +/** + * xa_is_err() - Report whether an XArray operation returned an error + * @entry: Result from calling an XArray function + * + * If an XArray operation cannot complete an operation, it will return + * a special value indicating an error. This function tells you + * whether an error occurred; xa_err() tells you which error occurred. + * + * Context: Any context. + * Return: %true if the entry indicates an error. + */ +static inline bool xa_is_err(const void *entry) +{ + return unlikely(xa_is_internal(entry) && + entry >= xa_mk_internal(-MAX_ERRNO)); +} + +/** + * xa_err() - Turn an XArray result into an errno. + * @entry: Result from calling an XArray function. + * + * If an XArray operation cannot complete an operation, it will return + * a special pointer value which encodes an errno. This function extracts + * the errno from the pointer value, or returns 0 if the pointer does not + * represent an errno. + * + * Context: Any context. + * Return: A negative errno or 0. + */ +static inline int xa_err(void *entry) +{ + /* xa_to_internal() would not do sign extension. */ + if (xa_is_err(entry)) + return (long)entry >> 2; + return 0; +} + +/** + * struct xa_limit - Represents a range of IDs. + * @min: The lowest ID to allocate (inclusive). + * @max: The maximum ID to allocate (inclusive). + * + * This structure is used either directly or via the XA_LIMIT() macro + * to communicate the range of IDs that are valid for allocation. + * Two common ranges are predefined for you: + * * xa_limit_32b - [0 - UINT_MAX] + * * xa_limit_31b - [0 - INT_MAX] + */ +struct xa_limit { + u32 max; + u32 min; +}; + +#define XA_LIMIT(_min, _max) (struct xa_limit) { .min = _min, .max = _max } + +#define xa_limit_32b XA_LIMIT(0, UINT_MAX) +#define xa_limit_31b XA_LIMIT(0, INT_MAX) + +typedef unsigned __bitwise xa_mark_t; +#define XA_MARK_0 ((__force xa_mark_t)0U) +#define XA_MARK_1 ((__force xa_mark_t)1U) +#define XA_MARK_2 ((__force xa_mark_t)2U) +#define XA_PRESENT ((__force xa_mark_t)8U) +#define XA_MARK_MAX XA_MARK_2 +#define XA_FREE_MARK XA_MARK_0 + +enum xa_lock_type { + XA_LOCK_IRQ = 1, + XA_LOCK_BH = 2, +}; + +/* + * Values for xa_flags. The radix tree stores its GFP flags in the xa_flags, + * and we remain compatible with that. + */ +#define XA_FLAGS_LOCK_IRQ ((__force gfp_t)XA_LOCK_IRQ) +#define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH) +#define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) +#define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U) +#define XA_FLAGS_ALLOC_WRAPPED ((__force gfp_t)16U) +#define XA_FLAGS_ACCOUNT ((__force gfp_t)32U) +#define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ + (__force unsigned)(mark))) + +/* ALLOC is for a normal 0-based alloc. ALLOC1 is for an 1-based alloc */ +#define XA_FLAGS_ALLOC (XA_FLAGS_TRACK_FREE | XA_FLAGS_MARK(XA_FREE_MARK)) +#define XA_FLAGS_ALLOC1 (XA_FLAGS_TRACK_FREE | XA_FLAGS_ZERO_BUSY) + +/** + * struct xarray - The anchor of the XArray. + * @xa_lock: Lock that protects the contents of the XArray. + * + * To use the xarray, define it statically or embed it in your data structure. + * It is a very small data structure, so it does not usually make sense to + * allocate it separately and keep a pointer to it in your data structure. + * + * You may use the xa_lock to protect your own data structures as well. + */ +/* + * If all of the entries in the array are NULL, @xa_head is a NULL pointer. + * If the only non-NULL entry in the array is at index 0, @xa_head is that + * entry. If any other entry in the array is non-NULL, @xa_head points + * to an @xa_node. + */ +struct xarray { + spinlock_t xa_lock; +/* private: The rest of the data structure is not to be used directly. */ + gfp_t xa_flags; + void __rcu * xa_head; +}; + +#define XARRAY_INIT(name, flags) { \ + .xa_lock = __SPIN_LOCK_UNLOCKED(name.xa_lock), \ + .xa_flags = flags, \ + .xa_head = NULL, \ +} + +/** + * DEFINE_XARRAY_FLAGS() - Define an XArray with custom flags. + * @name: A string that names your XArray. + * @flags: XA_FLAG values. + * + * This is intended for file scope definitions of XArrays. It declares + * and initialises an empty XArray with the chosen name and flags. It is + * equivalent to calling xa_init_flags() on the array, but it does the + * initialisation at compiletime instead of runtime. + */ +#define DEFINE_XARRAY_FLAGS(name, flags) \ + struct xarray name = XARRAY_INIT(name, flags) + +/** + * DEFINE_XARRAY() - Define an XArray. + * @name: A string that names your XArray. + * + * This is intended for file scope definitions of XArrays. It declares + * and initialises an empty XArray with the chosen name. It is equivalent + * to calling xa_init() on the array, but it does the initialisation at + * compiletime instead of runtime. + */ +#define DEFINE_XARRAY(name) DEFINE_XARRAY_FLAGS(name, 0) + +/** + * DEFINE_XARRAY_ALLOC() - Define an XArray which allocates IDs starting at 0. + * @name: A string that names your XArray. + * + * This is intended for file scope definitions of allocating XArrays. + * See also DEFINE_XARRAY(). + */ +#define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC) + +/** + * DEFINE_XARRAY_ALLOC1() - Define an XArray which allocates IDs starting at 1. + * @name: A string that names your XArray. + * + * This is intended for file scope definitions of allocating XArrays. + * See also DEFINE_XARRAY(). + */ +#define DEFINE_XARRAY_ALLOC1(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC1) + +void *xa_load(struct xarray *, unsigned long index); +void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); +void *xa_erase(struct xarray *, unsigned long index); +void *xa_store_range(struct xarray *, unsigned long first, unsigned long last, + void *entry, gfp_t); +bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t); +void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); +void xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); +void *xa_find(struct xarray *xa, unsigned long *index, + unsigned long max, xa_mark_t) __attribute__((nonnull(2))); +void *xa_find_after(struct xarray *xa, unsigned long *index, + unsigned long max, xa_mark_t) __attribute__((nonnull(2))); +unsigned int xa_extract(struct xarray *, void **dst, unsigned long start, + unsigned long max, unsigned int n, xa_mark_t); +void xa_destroy(struct xarray *); + +/** + * xa_init_flags() - Initialise an empty XArray with flags. + * @xa: XArray. + * @flags: XA_FLAG values. + * + * If you need to initialise an XArray with special flags (eg you need + * to take the lock from interrupt context), use this function instead + * of xa_init(). + * + * Context: Any context. + */ +static inline void xa_init_flags(struct xarray *xa, gfp_t flags) +{ + spin_lock_init(&xa->xa_lock); + xa->xa_flags = flags; + xa->xa_head = NULL; +} + +/** + * xa_init() - Initialise an empty XArray. + * @xa: XArray. + * + * An empty XArray is full of NULL entries. + * + * Context: Any context. + */ +static inline void xa_init(struct xarray *xa) +{ + xa_init_flags(xa, 0); +} + +/** + * xa_empty() - Determine if an array has any present entries. + * @xa: XArray. + * + * Context: Any context. + * Return: %true if the array contains only NULL pointers. + */ +static inline bool xa_empty(const struct xarray *xa) +{ + return xa->xa_head == NULL; +} + +/** + * xa_marked() - Inquire whether any entry in this array has a mark set + * @xa: Array + * @mark: Mark value + * + * Context: Any context. + * Return: %true if any entry has this mark set. + */ +static inline bool xa_marked(const struct xarray *xa, xa_mark_t mark) +{ + return xa->xa_flags & XA_FLAGS_MARK(mark); +} + +/** + * xa_for_each_start() - Iterate over a portion of an XArray. + * @xa: XArray. + * @index: Index of @entry. + * @entry: Entry retrieved from array. + * @start: First index to retrieve from array. + * + * During the iteration, @entry will have the value of the entry stored + * in @xa at @index. You may modify @index during the iteration if you + * want to skip or reprocess indices. It is safe to modify the array + * during the iteration. At the end of the iteration, @entry will be set + * to NULL and @index will have a value less than or equal to max. + * + * xa_for_each_start() is O(n.log(n)) while xas_for_each() is O(n). You have + * to handle your own locking with xas_for_each(), and if you have to unlock + * after each iteration, it will also end up being O(n.log(n)). + * xa_for_each_start() will spin if it hits a retry entry; if you intend to + * see retry entries, you should use the xas_for_each() iterator instead. + * The xas_for_each() iterator will expand into more inline code than + * xa_for_each_start(). + * + * Context: Any context. Takes and releases the RCU lock. + */ +#define xa_for_each_start(xa, index, entry, start) \ + for (index = start, \ + entry = xa_find(xa, &index, ULONG_MAX, XA_PRESENT); \ + entry; \ + entry = xa_find_after(xa, &index, ULONG_MAX, XA_PRESENT)) + +/** + * xa_for_each() - Iterate over present entries in an XArray. + * @xa: XArray. + * @index: Index of @entry. + * @entry: Entry retrieved from array. + * + * During the iteration, @entry will have the value of the entry stored + * in @xa at @index. You may modify @index during the iteration if you want + * to skip or reprocess indices. It is safe to modify the array during the + * iteration. At the end of the iteration, @entry will be set to NULL and + * @index will have a value less than or equal to max. + * + * xa_for_each() is O(n.log(n)) while xas_for_each() is O(n). You have + * to handle your own locking with xas_for_each(), and if you have to unlock + * after each iteration, it will also end up being O(n.log(n)). xa_for_each() + * will spin if it hits a retry entry; if you intend to see retry entries, + * you should use the xas_for_each() iterator instead. The xas_for_each() + * iterator will expand into more inline code than xa_for_each(). + * + * Context: Any context. Takes and releases the RCU lock. + */ +#define xa_for_each(xa, index, entry) \ + xa_for_each_start(xa, index, entry, 0) + +/** + * xa_for_each_marked() - Iterate over marked entries in an XArray. + * @xa: XArray. + * @index: Index of @entry. + * @entry: Entry retrieved from array. + * @filter: Selection criterion. + * + * During the iteration, @entry will have the value of the entry stored + * in @xa at @index. The iteration will skip all entries in the array + * which do not match @filter. You may modify @index during the iteration + * if you want to skip or reprocess indices. It is safe to modify the array + * during the iteration. At the end of the iteration, @entry will be set to + * NULL and @index will have a value less than or equal to max. + * + * xa_for_each_marked() is O(n.log(n)) while xas_for_each_marked() is O(n). + * You have to handle your own locking with xas_for_each(), and if you have + * to unlock after each iteration, it will also end up being O(n.log(n)). + * xa_for_each_marked() will spin if it hits a retry entry; if you intend to + * see retry entries, you should use the xas_for_each_marked() iterator + * instead. The xas_for_each_marked() iterator will expand into more inline + * code than xa_for_each_marked(). + * + * Context: Any context. Takes and releases the RCU lock. + */ +#define xa_for_each_marked(xa, index, entry, filter) \ + for (index = 0, entry = xa_find(xa, &index, ULONG_MAX, filter); \ + entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter)) + +#define xa_trylock(xa) spin_trylock(&(xa)->xa_lock) +#define xa_lock(xa) spin_lock(&(xa)->xa_lock) +#define xa_unlock(xa) spin_unlock(&(xa)->xa_lock) +#define xa_lock_bh(xa) spin_lock_bh(&(xa)->xa_lock) +#define xa_unlock_bh(xa) spin_unlock_bh(&(xa)->xa_lock) +#define xa_lock_irq(xa) spin_lock_irq(&(xa)->xa_lock) +#define xa_unlock_irq(xa) spin_unlock_irq(&(xa)->xa_lock) +#define xa_lock_irqsave(xa, flags) \ + spin_lock_irqsave(&(xa)->xa_lock, flags) +#define xa_unlock_irqrestore(xa, flags) \ + spin_unlock_irqrestore(&(xa)->xa_lock, flags) + +/* + * Versions of the normal API which require the caller to hold the + * xa_lock. If the GFP flags allow it, they will drop the lock to + * allocate memory, then reacquire it afterwards. These functions + * may also re-enable interrupts if the XArray flags indicate the + * locking should be interrupt safe. + */ +void *__xa_erase(struct xarray *, unsigned long index); +void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); +void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old, + void *entry, gfp_t); +int __must_check __xa_insert(struct xarray *, unsigned long index, + void *entry, gfp_t); +int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry, + struct xa_limit, gfp_t); +int __must_check __xa_alloc_cyclic(struct xarray *, u32 *id, void *entry, + struct xa_limit, u32 *next, gfp_t); +void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); +void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); + +/** + * xa_store_bh() - Store this entry in the XArray. + * @xa: XArray. + * @index: Index into array. + * @entry: New entry. + * @gfp: Memory allocation flags. + * + * This function is like calling xa_store() except it disables softirqs + * while holding the array lock. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. + * Return: The entry which used to be at this index. + */ +static inline void *xa_store_bh(struct xarray *xa, unsigned long index, + void *entry, gfp_t gfp) +{ + void *curr; + + xa_lock_bh(xa); + curr = __xa_store(xa, index, entry, gfp); + xa_unlock_bh(xa); + + return curr; +} + +/** + * xa_store_irq() - Store this entry in the XArray. + * @xa: XArray. + * @index: Index into array. + * @entry: New entry. + * @gfp: Memory allocation flags. + * + * This function is like calling xa_store() except it disables interrupts + * while holding the array lock. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. + * Return: The entry which used to be at this index. + */ +static inline void *xa_store_irq(struct xarray *xa, unsigned long index, + void *entry, gfp_t gfp) +{ + void *curr; + + xa_lock_irq(xa); + curr = __xa_store(xa, index, entry, gfp); + xa_unlock_irq(xa); + + return curr; +} + +/** + * xa_erase_bh() - Erase this entry from the XArray. + * @xa: XArray. + * @index: Index of entry. + * + * After this function returns, loading from @index will return %NULL. + * If the index is part of a multi-index entry, all indices will be erased + * and none of the entries will be part of a multi-index entry. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. + * Return: The entry which used to be at this index. + */ +static inline void *xa_erase_bh(struct xarray *xa, unsigned long index) +{ + void *entry; + + xa_lock_bh(xa); + entry = __xa_erase(xa, index); + xa_unlock_bh(xa); + + return entry; +} + +/** + * xa_erase_irq() - Erase this entry from the XArray. + * @xa: XArray. + * @index: Index of entry. + * + * After this function returns, loading from @index will return %NULL. + * If the index is part of a multi-index entry, all indices will be erased + * and none of the entries will be part of a multi-index entry. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. + * Return: The entry which used to be at this index. + */ +static inline void *xa_erase_irq(struct xarray *xa, unsigned long index) +{ + void *entry; + + xa_lock_irq(xa); + entry = __xa_erase(xa, index); + xa_unlock_irq(xa); + + return entry; +} + +/** + * xa_cmpxchg() - Conditionally replace an entry in the XArray. + * @xa: XArray. + * @index: Index into array. + * @old: Old value to test against. + * @entry: New value to place in array. + * @gfp: Memory allocation flags. + * + * If the entry at @index is the same as @old, replace it with @entry. + * If the return value is equal to @old, then the exchange was successful. + * + * Context: Any context. Takes and releases the xa_lock. May sleep + * if the @gfp flags permit. + * Return: The old value at this index or xa_err() if an error happened. + */ +static inline void *xa_cmpxchg(struct xarray *xa, unsigned long index, + void *old, void *entry, gfp_t gfp) +{ + void *curr; + + xa_lock(xa); + curr = __xa_cmpxchg(xa, index, old, entry, gfp); + xa_unlock(xa); + + return curr; +} + +/** + * xa_cmpxchg_bh() - Conditionally replace an entry in the XArray. + * @xa: XArray. + * @index: Index into array. + * @old: Old value to test against. + * @entry: New value to place in array. + * @gfp: Memory allocation flags. + * + * This function is like calling xa_cmpxchg() except it disables softirqs + * while holding the array lock. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. May sleep if the @gfp flags permit. + * Return: The old value at this index or xa_err() if an error happened. + */ +static inline void *xa_cmpxchg_bh(struct xarray *xa, unsigned long index, + void *old, void *entry, gfp_t gfp) +{ + void *curr; + + xa_lock_bh(xa); + curr = __xa_cmpxchg(xa, index, old, entry, gfp); + xa_unlock_bh(xa); + + return curr; +} + +/** + * xa_cmpxchg_irq() - Conditionally replace an entry in the XArray. + * @xa: XArray. + * @index: Index into array. + * @old: Old value to test against. + * @entry: New value to place in array. + * @gfp: Memory allocation flags. + * + * This function is like calling xa_cmpxchg() except it disables interrupts + * while holding the array lock. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. May sleep if the @gfp flags permit. + * Return: The old value at this index or xa_err() if an error happened. + */ +static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index, + void *old, void *entry, gfp_t gfp) +{ + void *curr; + + xa_lock_irq(xa); + curr = __xa_cmpxchg(xa, index, old, entry, gfp); + xa_unlock_irq(xa); + + return curr; +} + +/** + * xa_insert() - Store this entry in the XArray unless another entry is + * already present. + * @xa: XArray. + * @index: Index into array. + * @entry: New entry. + * @gfp: Memory allocation flags. + * + * Inserting a NULL entry will store a reserved entry (like xa_reserve()) + * if no entry is present. Inserting will fail if a reserved entry is + * present, even though loading from this index will return NULL. + * + * Context: Any context. Takes and releases the xa_lock. May sleep if + * the @gfp flags permit. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. + * -ENOMEM if memory could not be allocated. + */ +static inline int __must_check xa_insert(struct xarray *xa, + unsigned long index, void *entry, gfp_t gfp) +{ + int err; + + xa_lock(xa); + err = __xa_insert(xa, index, entry, gfp); + xa_unlock(xa); + + return err; +} + +/** + * xa_insert_bh() - Store this entry in the XArray unless another entry is + * already present. + * @xa: XArray. + * @index: Index into array. + * @entry: New entry. + * @gfp: Memory allocation flags. + * + * Inserting a NULL entry will store a reserved entry (like xa_reserve()) + * if no entry is present. Inserting will fail if a reserved entry is + * present, even though loading from this index will return NULL. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. May sleep if the @gfp flags permit. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. + * -ENOMEM if memory could not be allocated. + */ +static inline int __must_check xa_insert_bh(struct xarray *xa, + unsigned long index, void *entry, gfp_t gfp) +{ + int err; + + xa_lock_bh(xa); + err = __xa_insert(xa, index, entry, gfp); + xa_unlock_bh(xa); + + return err; +} + +/** + * xa_insert_irq() - Store this entry in the XArray unless another entry is + * already present. + * @xa: XArray. + * @index: Index into array. + * @entry: New entry. + * @gfp: Memory allocation flags. + * + * Inserting a NULL entry will store a reserved entry (like xa_reserve()) + * if no entry is present. Inserting will fail if a reserved entry is + * present, even though loading from this index will return NULL. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. May sleep if the @gfp flags permit. + * Return: 0 if the store succeeded. -EBUSY if another entry was present. + * -ENOMEM if memory could not be allocated. + */ +static inline int __must_check xa_insert_irq(struct xarray *xa, + unsigned long index, void *entry, gfp_t gfp) +{ + int err; + + xa_lock_irq(xa); + err = __xa_insert(xa, index, entry, gfp); + xa_unlock_irq(xa); + + return err; +} + +/** + * xa_alloc() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * + * Context: Any context. Takes and releases the xa_lock. May sleep if + * the @gfp flags permit. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. + */ +static inline __must_check int xa_alloc(struct xarray *xa, u32 *id, + void *entry, struct xa_limit limit, gfp_t gfp) +{ + int err; + + xa_lock(xa); + err = __xa_alloc(xa, id, entry, limit, gfp); + xa_unlock(xa); + + return err; +} + +/** + * xa_alloc_bh() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. May sleep if the @gfp flags permit. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. + */ +static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id, + void *entry, struct xa_limit limit, gfp_t gfp) +{ + int err; + + xa_lock_bh(xa); + err = __xa_alloc(xa, id, entry, limit, gfp); + xa_unlock_bh(xa); + + return err; +} + +/** + * xa_alloc_irq() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. May sleep if the @gfp flags permit. + * Return: 0 on success, -ENOMEM if memory could not be allocated or + * -EBUSY if there are no free entries in @limit. + */ +static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id, + void *entry, struct xa_limit limit, gfp_t gfp) +{ + int err; + + xa_lock_irq(xa); + err = __xa_alloc(xa, id, entry, limit, gfp); + xa_unlock_irq(xa); + + return err; +} + +/** + * xa_alloc_cyclic() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Any context. Takes and releases the xa_lock. May sleep if + * the @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +static inline int xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + int err; + + xa_lock(xa); + err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); + xa_unlock(xa); + + return err; +} + +/** + * xa_alloc_cyclic_bh() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. May sleep if the @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +static inline int xa_alloc_cyclic_bh(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + int err; + + xa_lock_bh(xa); + err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); + xa_unlock_bh(xa); + + return err; +} + +/** + * xa_alloc_cyclic_irq() - Find somewhere to store this entry in the XArray. + * @xa: XArray. + * @id: Pointer to ID. + * @entry: New entry. + * @limit: Range of allocated ID. + * @next: Pointer to next ID to allocate. + * @gfp: Memory allocation flags. + * + * Finds an empty entry in @xa between @limit.min and @limit.max, + * stores the index into the @id pointer, then stores the entry at + * that index. A concurrent lookup will not see an uninitialised @id. + * The search for an empty entry will start at @next and will wrap + * around if necessary. + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. May sleep if the @gfp flags permit. + * Return: 0 if the allocation succeeded without wrapping. 1 if the + * allocation succeeded after wrapping, -ENOMEM if memory could not be + * allocated or -EBUSY if there are no free entries in @limit. + */ +static inline int xa_alloc_cyclic_irq(struct xarray *xa, u32 *id, void *entry, + struct xa_limit limit, u32 *next, gfp_t gfp) +{ + int err; + + xa_lock_irq(xa); + err = __xa_alloc_cyclic(xa, id, entry, limit, next, gfp); + xa_unlock_irq(xa); + + return err; +} + +/** + * xa_reserve() - Reserve this index in the XArray. + * @xa: XArray. + * @index: Index into array. + * @gfp: Memory allocation flags. + * + * Ensures there is somewhere to store an entry at @index in the array. + * If there is already something stored at @index, this function does + * nothing. If there was nothing there, the entry is marked as reserved. + * Loading from a reserved entry returns a %NULL pointer. + * + * If you do not use the entry that you have reserved, call xa_release() + * or xa_erase() to free any unnecessary memory. + * + * Context: Any context. Takes and releases the xa_lock. + * May sleep if the @gfp flags permit. + * Return: 0 if the reservation succeeded or -ENOMEM if it failed. + */ +static inline __must_check +int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp) +{ + return xa_err(xa_cmpxchg(xa, index, NULL, XA_ZERO_ENTRY, gfp)); +} + +/** + * xa_reserve_bh() - Reserve this index in the XArray. + * @xa: XArray. + * @index: Index into array. + * @gfp: Memory allocation flags. + * + * A softirq-disabling version of xa_reserve(). + * + * Context: Any context. Takes and releases the xa_lock while + * disabling softirqs. + * Return: 0 if the reservation succeeded or -ENOMEM if it failed. + */ +static inline __must_check +int xa_reserve_bh(struct xarray *xa, unsigned long index, gfp_t gfp) +{ + return xa_err(xa_cmpxchg_bh(xa, index, NULL, XA_ZERO_ENTRY, gfp)); +} + +/** + * xa_reserve_irq() - Reserve this index in the XArray. + * @xa: XArray. + * @index: Index into array. + * @gfp: Memory allocation flags. + * + * An interrupt-disabling version of xa_reserve(). + * + * Context: Process context. Takes and releases the xa_lock while + * disabling interrupts. + * Return: 0 if the reservation succeeded or -ENOMEM if it failed. + */ +static inline __must_check +int xa_reserve_irq(struct xarray *xa, unsigned long index, gfp_t gfp) +{ + return xa_err(xa_cmpxchg_irq(xa, index, NULL, XA_ZERO_ENTRY, gfp)); +} + +/** + * xa_release() - Release a reserved entry. + * @xa: XArray. + * @index: Index of entry. + * + * After calling xa_reserve(), you can call this function to release the + * reservation. If the entry at @index has been stored to, this function + * will do nothing. + */ +static inline void xa_release(struct xarray *xa, unsigned long index) +{ + xa_cmpxchg(xa, index, XA_ZERO_ENTRY, NULL, 0); +} + +/* Everything below here is the Advanced API. Proceed with caution. */ + +/* + * The xarray is constructed out of a set of 'chunks' of pointers. Choosing + * the best chunk size requires some tradeoffs. A power of two recommends + * itself so that we can walk the tree based purely on shifts and masks. + * Generally, the larger the better; as the number of slots per level of the + * tree increases, the less tall the tree needs to be. But that needs to be + * balanced against the memory consumption of each node. On a 64-bit system, + * xa_node is currently 576 bytes, and we get 7 of them per 4kB page. If we + * doubled the number of slots per node, we'd get only 3 nodes per 4kB page. + */ +#ifndef XA_CHUNK_SHIFT +#define XA_CHUNK_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) +#endif +#define XA_CHUNK_SIZE (1UL << XA_CHUNK_SHIFT) +#define XA_CHUNK_MASK (XA_CHUNK_SIZE - 1) +#define XA_MAX_MARKS 3 +#define XA_MARK_LONGS DIV_ROUND_UP(XA_CHUNK_SIZE, BITS_PER_LONG) + +/* + * @count is the count of every non-NULL element in the ->slots array + * whether that is a value entry, a retry entry, a user pointer, + * a sibling entry or a pointer to the next level of the tree. + * @nr_values is the count of every element in ->slots which is + * either a value entry or a sibling of a value entry. + */ +struct xa_node { + unsigned char shift; /* Bits remaining in each slot */ + unsigned char offset; /* Slot offset in parent */ + unsigned char count; /* Total entry count */ + unsigned char nr_values; /* Value entry count */ + struct xa_node __rcu *parent; /* NULL at top of tree */ + struct xarray *array; /* The array we belong to */ + union { + struct list_head private_list; /* For tree user */ + struct rcu_head rcu_head; /* Used when freeing node */ + }; + void __rcu *slots[XA_CHUNK_SIZE]; + union { + unsigned long tags[XA_MAX_MARKS][XA_MARK_LONGS]; + unsigned long marks[XA_MAX_MARKS][XA_MARK_LONGS]; + }; +}; + +void xa_dump(const struct xarray *); +void xa_dump_node(const struct xa_node *); + +#ifdef XA_DEBUG +#define XA_BUG_ON(xa, x) do { \ + if (x) { \ + xa_dump(xa); \ + BUG(); \ + } \ + } while (0) +#define XA_NODE_BUG_ON(node, x) do { \ + if (x) { \ + if (node) xa_dump_node(node); \ + BUG(); \ + } \ + } while (0) +#else +#define XA_BUG_ON(xa, x) do { } while (0) +#define XA_NODE_BUG_ON(node, x) do { } while (0) +#endif + +/* Private */ +static inline void *xa_head(const struct xarray *xa) +{ + return rcu_dereference_check(xa->xa_head, + lockdep_is_held(&xa->xa_lock)); +} + +/* Private */ +static inline void *xa_head_locked(const struct xarray *xa) +{ + return rcu_dereference_protected(xa->xa_head, + lockdep_is_held(&xa->xa_lock)); +} + +/* Private */ +static inline void *xa_entry(const struct xarray *xa, + const struct xa_node *node, unsigned int offset) +{ + XA_NODE_BUG_ON(node, offset >= XA_CHUNK_SIZE); + return rcu_dereference_check(node->slots[offset], + lockdep_is_held(&xa->xa_lock)); +} + +/* Private */ +static inline void *xa_entry_locked(const struct xarray *xa, + const struct xa_node *node, unsigned int offset) +{ + XA_NODE_BUG_ON(node, offset >= XA_CHUNK_SIZE); + return rcu_dereference_protected(node->slots[offset], + lockdep_is_held(&xa->xa_lock)); +} + +/* Private */ +static inline struct xa_node *xa_parent(const struct xarray *xa, + const struct xa_node *node) +{ + return rcu_dereference_check(node->parent, + lockdep_is_held(&xa->xa_lock)); +} + +/* Private */ +static inline struct xa_node *xa_parent_locked(const struct xarray *xa, + const struct xa_node *node) +{ + return rcu_dereference_protected(node->parent, + lockdep_is_held(&xa->xa_lock)); +} + +/* Private */ +static inline void *xa_mk_node(const struct xa_node *node) +{ + return (void *)((unsigned long)node | 2); +} + +/* Private */ +static inline struct xa_node *xa_to_node(const void *entry) +{ + return (struct xa_node *)((unsigned long)entry - 2); +} + +/* Private */ +static inline bool xa_is_node(const void *entry) +{ + return xa_is_internal(entry) && (unsigned long)entry > 4096; +} + +/* Private */ +static inline void *xa_mk_sibling(unsigned int offset) +{ + return xa_mk_internal(offset); +} + +/* Private */ +static inline unsigned long xa_to_sibling(const void *entry) +{ + return xa_to_internal(entry); +} + +/** + * xa_is_sibling() - Is the entry a sibling entry? + * @entry: Entry retrieved from the XArray + * + * Return: %true if the entry is a sibling entry. + */ +static inline bool xa_is_sibling(const void *entry) +{ + return IS_ENABLED(CONFIG_XARRAY_MULTI) && xa_is_internal(entry) && + (entry < xa_mk_sibling(XA_CHUNK_SIZE - 1)); +} + +#define XA_RETRY_ENTRY xa_mk_internal(256) + +/** + * xa_is_retry() - Is the entry a retry entry? + * @entry: Entry retrieved from the XArray + * + * Return: %true if the entry is a retry entry. + */ +static inline bool xa_is_retry(const void *entry) +{ + return unlikely(entry == XA_RETRY_ENTRY); +} + +/** + * xa_is_advanced() - Is the entry only permitted for the advanced API? + * @entry: Entry to be stored in the XArray. + * + * Return: %true if the entry cannot be stored by the normal API. + */ +static inline bool xa_is_advanced(const void *entry) +{ + return xa_is_internal(entry) && (entry <= XA_RETRY_ENTRY); +} + +/** + * typedef xa_update_node_t - A callback function from the XArray. + * @node: The node which is being processed + * + * This function is called every time the XArray updates the count of + * present and value entries in a node. It allows advanced users to + * maintain the private_list in the node. + * + * Context: The xa_lock is held and interrupts may be disabled. + * Implementations should not drop the xa_lock, nor re-enable + * interrupts. + */ +typedef void (*xa_update_node_t)(struct xa_node *node); + +/* + * The xa_state is opaque to its users. It contains various different pieces + * of state involved in the current operation on the XArray. It should be + * declared on the stack and passed between the various internal routines. + * The various elements in it should not be accessed directly, but only + * through the provided accessor functions. The below documentation is for + * the benefit of those working on the code, not for users of the XArray. + * + * @xa_node usually points to the xa_node containing the slot we're operating + * on (and @xa_offset is the offset in the slots array). If there is a + * single entry in the array at index 0, there are no allocated xa_nodes to + * point to, and so we store %NULL in @xa_node. @xa_node is set to + * the value %XAS_RESTART if the xa_state is not walked to the correct + * position in the tree of nodes for this operation. If an error occurs + * during an operation, it is set to an %XAS_ERROR value. If we run off the + * end of the allocated nodes, it is set to %XAS_BOUNDS. + */ +struct xa_state { + struct xarray *xa; + unsigned long xa_index; + unsigned char xa_shift; + unsigned char xa_sibs; + unsigned char xa_offset; + unsigned char xa_pad; /* Helps gcc generate better code */ + struct xa_node *xa_node; + struct xa_node *xa_alloc; + xa_update_node_t xa_update; +}; + +/* + * We encode errnos in the xas->xa_node. If an error has happened, we need to + * drop the lock to fix it, and once we've done so the xa_state is invalid. + */ +#define XA_ERROR(errno) ((struct xa_node *)(((unsigned long)errno << 2) | 2UL)) +#define XAS_BOUNDS ((struct xa_node *)1UL) +#define XAS_RESTART ((struct xa_node *)3UL) + +#define __XA_STATE(array, index, shift, sibs) { \ + .xa = array, \ + .xa_index = index, \ + .xa_shift = shift, \ + .xa_sibs = sibs, \ + .xa_offset = 0, \ + .xa_pad = 0, \ + .xa_node = XAS_RESTART, \ + .xa_alloc = NULL, \ + .xa_update = NULL \ +} + +/** + * XA_STATE() - Declare an XArray operation state. + * @name: Name of this operation state (usually xas). + * @array: Array to operate on. + * @index: Initial index of interest. + * + * Declare and initialise an xa_state on the stack. + */ +#define XA_STATE(name, array, index) \ + struct xa_state name = __XA_STATE(array, index, 0, 0) + +/** + * XA_STATE_ORDER() - Declare an XArray operation state. + * @name: Name of this operation state (usually xas). + * @array: Array to operate on. + * @index: Initial index of interest. + * @order: Order of entry. + * + * Declare and initialise an xa_state on the stack. This variant of + * XA_STATE() allows you to specify the 'order' of the element you + * want to operate on.` + */ +#define XA_STATE_ORDER(name, array, index, order) \ + struct xa_state name = __XA_STATE(array, \ + (index >> order) << order, \ + order - (order % XA_CHUNK_SHIFT), \ + (1U << (order % XA_CHUNK_SHIFT)) - 1) + +#define xas_marked(xas, mark) xa_marked((xas)->xa, (mark)) +#define xas_trylock(xas) xa_trylock((xas)->xa) +#define xas_lock(xas) xa_lock((xas)->xa) +#define xas_unlock(xas) xa_unlock((xas)->xa) +#define xas_lock_bh(xas) xa_lock_bh((xas)->xa) +#define xas_unlock_bh(xas) xa_unlock_bh((xas)->xa) +#define xas_lock_irq(xas) xa_lock_irq((xas)->xa) +#define xas_unlock_irq(xas) xa_unlock_irq((xas)->xa) +#define xas_lock_irqsave(xas, flags) \ + xa_lock_irqsave((xas)->xa, flags) +#define xas_unlock_irqrestore(xas, flags) \ + xa_unlock_irqrestore((xas)->xa, flags) + +/** + * xas_error() - Return an errno stored in the xa_state. + * @xas: XArray operation state. + * + * Return: 0 if no error has been noted. A negative errno if one has. + */ +static inline int xas_error(const struct xa_state *xas) +{ + return xa_err(xas->xa_node); +} + +/** + * xas_set_err() - Note an error in the xa_state. + * @xas: XArray operation state. + * @err: Negative error number. + * + * Only call this function with a negative @err; zero or positive errors + * will probably not behave the way you think they should. If you want + * to clear the error from an xa_state, use xas_reset(). + */ +static inline void xas_set_err(struct xa_state *xas, long err) +{ + xas->xa_node = XA_ERROR(err); +} + +/** + * xas_invalid() - Is the xas in a retry or error state? + * @xas: XArray operation state. + * + * Return: %true if the xas cannot be used for operations. + */ +static inline bool xas_invalid(const struct xa_state *xas) +{ + return (unsigned long)xas->xa_node & 3; +} + +/** + * xas_valid() - Is the xas a valid cursor into the array? + * @xas: XArray operation state. + * + * Return: %true if the xas can be used for operations. + */ +static inline bool xas_valid(const struct xa_state *xas) +{ + return !xas_invalid(xas); +} + +/** + * xas_is_node() - Does the xas point to a node? + * @xas: XArray operation state. + * + * Return: %true if the xas currently references a node. + */ +static inline bool xas_is_node(const struct xa_state *xas) +{ + return xas_valid(xas) && xas->xa_node; +} + +/* True if the pointer is something other than a node */ +static inline bool xas_not_node(struct xa_node *node) +{ + return ((unsigned long)node & 3) || !node; +} + +/* True if the node represents RESTART or an error */ +static inline bool xas_frozen(struct xa_node *node) +{ + return (unsigned long)node & 2; +} + +/* True if the node represents head-of-tree, RESTART or BOUNDS */ +static inline bool xas_top(struct xa_node *node) +{ + return node <= XAS_RESTART; +} + +/** + * xas_reset() - Reset an XArray operation state. + * @xas: XArray operation state. + * + * Resets the error or walk state of the @xas so future walks of the + * array will start from the root. Use this if you have dropped the + * xarray lock and want to reuse the xa_state. + * + * Context: Any context. + */ +static inline void xas_reset(struct xa_state *xas) +{ + xas->xa_node = XAS_RESTART; +} + +/** + * xas_retry() - Retry the operation if appropriate. + * @xas: XArray operation state. + * @entry: Entry from xarray. + * + * The advanced functions may sometimes return an internal entry, such as + * a retry entry or a zero entry. This function sets up the @xas to restart + * the walk from the head of the array if needed. + * + * Context: Any context. + * Return: true if the operation needs to be retried. + */ +static inline bool xas_retry(struct xa_state *xas, const void *entry) +{ + if (xa_is_zero(entry)) + return true; + if (!xa_is_retry(entry)) + return false; + xas_reset(xas); + return true; +} + +void *xas_load(struct xa_state *); +void *xas_store(struct xa_state *, void *entry); +void *xas_find(struct xa_state *, unsigned long max); +void *xas_find_conflict(struct xa_state *); + +bool xas_get_mark(const struct xa_state *, xa_mark_t); +void xas_set_mark(const struct xa_state *, xa_mark_t); +void xas_clear_mark(const struct xa_state *, xa_mark_t); +void *xas_find_marked(struct xa_state *, unsigned long max, xa_mark_t); +void xas_init_marks(const struct xa_state *); + +bool xas_nomem(struct xa_state *, gfp_t); +void xas_pause(struct xa_state *); + +void xas_create_range(struct xa_state *); + +/** + * xas_reload() - Refetch an entry from the xarray. + * @xas: XArray operation state. + * + * Use this function to check that a previously loaded entry still has + * the same value. This is useful for the lockless pagecache lookup where + * we walk the array with only the RCU lock to protect us, lock the page, + * then check that the page hasn't moved since we looked it up. + * + * The caller guarantees that @xas is still valid. If it may be in an + * error or restart state, call xas_load() instead. + * + * Return: The entry at this location in the xarray. + */ +static inline void *xas_reload(struct xa_state *xas) +{ + struct xa_node *node = xas->xa_node; + + if (node) + return xa_entry(xas->xa, node, xas->xa_offset); + return xa_head(xas->xa); +} + +/** + * xas_set() - Set up XArray operation state for a different index. + * @xas: XArray operation state. + * @index: New index into the XArray. + * + * Move the operation state to refer to a different index. This will + * have the effect of starting a walk from the top; see xas_next() + * to move to an adjacent index. + */ +static inline void xas_set(struct xa_state *xas, unsigned long index) +{ + xas->xa_index = index; + xas->xa_node = XAS_RESTART; +} + +/** + * xas_set_order() - Set up XArray operation state for a multislot entry. + * @xas: XArray operation state. + * @index: Target of the operation. + * @order: Entry occupies 2^@order indices. + */ +static inline void xas_set_order(struct xa_state *xas, unsigned long index, + unsigned int order) +{ +#ifdef CONFIG_XARRAY_MULTI + xas->xa_index = order < BITS_PER_LONG ? (index >> order) << order : 0; + xas->xa_shift = order - (order % XA_CHUNK_SHIFT); + xas->xa_sibs = (1 << (order % XA_CHUNK_SHIFT)) - 1; + xas->xa_node = XAS_RESTART; +#else + BUG_ON(order > 0); + xas_set(xas, index); +#endif +} + +/** + * xas_set_update() - Set up XArray operation state for a callback. + * @xas: XArray operation state. + * @update: Function to call when updating a node. + * + * The XArray can notify a caller after it has updated an xa_node. + * This is advanced functionality and is only needed by the page cache. + */ +static inline void xas_set_update(struct xa_state *xas, xa_update_node_t update) +{ + xas->xa_update = update; +} + +/** + * xas_next_entry() - Advance iterator to next present entry. + * @xas: XArray operation state. + * @max: Highest index to return. + * + * xas_next_entry() is an inline function to optimise xarray traversal for + * speed. It is equivalent to calling xas_find(), and will call xas_find() + * for all the hard cases. + * + * Return: The next present entry after the one currently referred to by @xas. + */ +static inline void *xas_next_entry(struct xa_state *xas, unsigned long max) +{ + struct xa_node *node = xas->xa_node; + void *entry; + + if (unlikely(xas_not_node(node) || node->shift || + xas->xa_offset != (xas->xa_index & XA_CHUNK_MASK))) + return xas_find(xas, max); + + do { + if (unlikely(xas->xa_index >= max)) + return xas_find(xas, max); + if (unlikely(xas->xa_offset == XA_CHUNK_MASK)) + return xas_find(xas, max); + entry = xa_entry(xas->xa, node, xas->xa_offset + 1); + if (unlikely(xa_is_internal(entry))) + return xas_find(xas, max); + xas->xa_offset++; + xas->xa_index++; + } while (!entry); + + return entry; +} + +/* Private */ +static inline unsigned int xas_find_chunk(struct xa_state *xas, bool advance, + xa_mark_t mark) +{ + unsigned long *addr = xas->xa_node->marks[(__force unsigned)mark]; + unsigned int offset = xas->xa_offset; + + if (advance) + offset++; + if (XA_CHUNK_SIZE == BITS_PER_LONG) { + if (offset < XA_CHUNK_SIZE) { + unsigned long data = *addr & (~0UL << offset); + if (data) + return __ffs(data); + } + return XA_CHUNK_SIZE; + } + + return find_next_bit(addr, XA_CHUNK_SIZE, offset); +} + +/** + * xas_next_marked() - Advance iterator to next marked entry. + * @xas: XArray operation state. + * @max: Highest index to return. + * @mark: Mark to search for. + * + * xas_next_marked() is an inline function to optimise xarray traversal for + * speed. It is equivalent to calling xas_find_marked(), and will call + * xas_find_marked() for all the hard cases. + * + * Return: The next marked entry after the one currently referred to by @xas. + */ +static inline void *xas_next_marked(struct xa_state *xas, unsigned long max, + xa_mark_t mark) +{ + struct xa_node *node = xas->xa_node; + unsigned int offset; + + if (unlikely(xas_not_node(node) || node->shift)) + return xas_find_marked(xas, max, mark); + offset = xas_find_chunk(xas, true, mark); + xas->xa_offset = offset; + xas->xa_index = (xas->xa_index & ~XA_CHUNK_MASK) + offset; + if (xas->xa_index > max) + return NULL; + if (offset == XA_CHUNK_SIZE) + return xas_find_marked(xas, max, mark); + return xa_entry(xas->xa, node, offset); +} + +/* + * If iterating while holding a lock, drop the lock and reschedule + * every %XA_CHECK_SCHED loops. + */ +enum { + XA_CHECK_SCHED = 4096, +}; + +/** + * xas_for_each() - Iterate over a range of an XArray. + * @xas: XArray operation state. + * @entry: Entry retrieved from the array. + * @max: Maximum index to retrieve from array. + * + * The loop body will be executed for each entry present in the xarray + * between the current xas position and @max. @entry will be set to + * the entry retrieved from the xarray. It is safe to delete entries + * from the array in the loop body. You should hold either the RCU lock + * or the xa_lock while iterating. If you need to drop the lock, call + * xas_pause() first. + */ +#define xas_for_each(xas, entry, max) \ + for (entry = xas_find(xas, max); entry; \ + entry = xas_next_entry(xas, max)) + +/** + * xas_for_each_marked() - Iterate over a range of an XArray. + * @xas: XArray operation state. + * @entry: Entry retrieved from the array. + * @max: Maximum index to retrieve from array. + * @mark: Mark to search for. + * + * The loop body will be executed for each marked entry in the xarray + * between the current xas position and @max. @entry will be set to + * the entry retrieved from the xarray. It is safe to delete entries + * from the array in the loop body. You should hold either the RCU lock + * or the xa_lock while iterating. If you need to drop the lock, call + * xas_pause() first. + */ +#define xas_for_each_marked(xas, entry, max, mark) \ + for (entry = xas_find_marked(xas, max, mark); entry; \ + entry = xas_next_marked(xas, max, mark)) + +/** + * xas_for_each_conflict() - Iterate over a range of an XArray. + * @xas: XArray operation state. + * @entry: Entry retrieved from the array. + * + * The loop body will be executed for each entry in the XArray that lies + * within the range specified by @xas. If the loop completes successfully, + * any entries that lie in this range will be replaced by @entry. The caller + * may break out of the loop; if they do so, the contents of the XArray will + * be unchanged. The operation may fail due to an out of memory condition. + * The caller may also call xa_set_err() to exit the loop while setting an + * error to record the reason. + */ +#define xas_for_each_conflict(xas, entry) \ + while ((entry = xas_find_conflict(xas))) + +void *__xas_next(struct xa_state *); +void *__xas_prev(struct xa_state *); + +/** + * xas_prev() - Move iterator to previous index. + * @xas: XArray operation state. + * + * If the @xas was in an error state, it will remain in an error state + * and this function will return %NULL. If the @xas has never been walked, + * it will have the effect of calling xas_load(). Otherwise one will be + * subtracted from the index and the state will be walked to the correct + * location in the array for the next operation. + * + * If the iterator was referencing index 0, this function wraps + * around to %ULONG_MAX. + * + * Return: The entry at the new index. This may be %NULL or an internal + * entry. + */ +static inline void *xas_prev(struct xa_state *xas) +{ + struct xa_node *node = xas->xa_node; + + if (unlikely(xas_not_node(node) || node->shift || + xas->xa_offset == 0)) + return __xas_prev(xas); + + xas->xa_index--; + xas->xa_offset--; + return xa_entry(xas->xa, node, xas->xa_offset); +} + +/** + * xas_next() - Move state to next index. + * @xas: XArray operation state. + * + * If the @xas was in an error state, it will remain in an error state + * and this function will return %NULL. If the @xas has never been walked, + * it will have the effect of calling xas_load(). Otherwise one will be + * added to the index and the state will be walked to the correct + * location in the array for the next operation. + * + * If the iterator was referencing index %ULONG_MAX, this function wraps + * around to 0. + * + * Return: The entry at the new index. This may be %NULL or an internal + * entry. + */ +static inline void *xas_next(struct xa_state *xas) +{ + struct xa_node *node = xas->xa_node; + + if (unlikely(xas_not_node(node) || node->shift || + xas->xa_offset == XA_CHUNK_MASK)) + return __xas_next(xas); + + xas->xa_index++; + xas->xa_offset++; + return xa_entry(xas->xa, node, xas->xa_offset); +} + +#endif /* _LINUX_XARRAY_H */ diff --git a/t/tree/include/trace/events/i2c.h b/t/tree/include/trace/events/i2c.h new file mode 100644 index 00000000..142a23c6 --- /dev/null +++ b/t/tree/include/trace/events/i2c.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* I2C message transfer tracepoints + * + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM i2c + +#if !defined(_TRACE_I2C_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_I2C_H + +#include +#include + +/* + * drivers/i2c/i2c-core-base.c + */ +extern int i2c_transfer_trace_reg(void); +extern void i2c_transfer_trace_unreg(void); + +/* + * __i2c_transfer() write request + */ +TRACE_EVENT_FN(i2c_write, + TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, + int num), + TP_ARGS(adap, msg, num), + TP_STRUCT__entry( + __field(int, adapter_nr ) + __field(__u16, msg_nr ) + __field(__u16, addr ) + __field(__u16, flags ) + __field(__u16, len ) + __dynamic_array(__u8, buf, msg->len) ), + TP_fast_assign( + __entry->adapter_nr = adap->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flags = msg->flags; + __entry->len = msg->len; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]", + __entry->adapter_nr, + __entry->msg_nr, + __entry->addr, + __entry->flags, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + i2c_transfer_trace_reg, + i2c_transfer_trace_unreg); + +/* + * __i2c_transfer() read request + */ +TRACE_EVENT_FN(i2c_read, + TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, + int num), + TP_ARGS(adap, msg, num), + TP_STRUCT__entry( + __field(int, adapter_nr ) + __field(__u16, msg_nr ) + __field(__u16, addr ) + __field(__u16, flags ) + __field(__u16, len ) + ), + TP_fast_assign( + __entry->adapter_nr = adap->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flags = msg->flags; + __entry->len = msg->len; + ), + TP_printk("i2c-%d #%u a=%03x f=%04x l=%u", + __entry->adapter_nr, + __entry->msg_nr, + __entry->addr, + __entry->flags, + __entry->len + ), + i2c_transfer_trace_reg, + i2c_transfer_trace_unreg); + +/* + * __i2c_transfer() read reply + */ +TRACE_EVENT_FN(i2c_reply, + TP_PROTO(const struct i2c_adapter *adap, const struct i2c_msg *msg, + int num), + TP_ARGS(adap, msg, num), + TP_STRUCT__entry( + __field(int, adapter_nr ) + __field(__u16, msg_nr ) + __field(__u16, addr ) + __field(__u16, flags ) + __field(__u16, len ) + __dynamic_array(__u8, buf, msg->len) ), + TP_fast_assign( + __entry->adapter_nr = adap->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flags = msg->flags; + __entry->len = msg->len; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("i2c-%d #%u a=%03x f=%04x l=%u [%*phD]", + __entry->adapter_nr, + __entry->msg_nr, + __entry->addr, + __entry->flags, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + i2c_transfer_trace_reg, + i2c_transfer_trace_unreg); + +/* + * __i2c_transfer() result + */ +TRACE_EVENT_FN(i2c_result, + TP_PROTO(const struct i2c_adapter *adap, int num, int ret), + TP_ARGS(adap, num, ret), + TP_STRUCT__entry( + __field(int, adapter_nr ) + __field(__u16, nr_msgs ) + __field(__s16, ret ) + ), + TP_fast_assign( + __entry->adapter_nr = adap->nr; + __entry->nr_msgs = num; + __entry->ret = ret; + ), + TP_printk("i2c-%d n=%u ret=%d", + __entry->adapter_nr, + __entry->nr_msgs, + __entry->ret + ), + i2c_transfer_trace_reg, + i2c_transfer_trace_unreg); + +#endif /* _TRACE_I2C_H */ + +/* This part must be outside protection */ +#include diff --git a/t/tree/include/uapi/linux/apm_bios.h b/t/tree/include/uapi/linux/apm_bios.h new file mode 100644 index 00000000..37ee11db --- /dev/null +++ b/t/tree/include/uapi/linux/apm_bios.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Include file for the interface to an APM BIOS + * Copyright 1994-2001 Stephen Rothwell (sfr@canb.auug.org.au) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef _UAPI_LINUX_APM_H +#define _UAPI_LINUX_APM_H + + +#include + +typedef unsigned short apm_event_t; +typedef unsigned short apm_eventinfo_t; + +struct apm_bios_info { + __u16 version; + __u16 cseg; + __u32 offset; + __u16 cseg_16; + __u16 dseg; + __u16 flags; + __u16 cseg_len; + __u16 cseg_16_len; + __u16 dseg_len; +}; + + +/* + * Power states + */ +#define APM_STATE_READY 0x0000 +#define APM_STATE_STANDBY 0x0001 +#define APM_STATE_SUSPEND 0x0002 +#define APM_STATE_OFF 0x0003 +#define APM_STATE_BUSY 0x0004 +#define APM_STATE_REJECT 0x0005 +#define APM_STATE_OEM_SYS 0x0020 +#define APM_STATE_OEM_DEV 0x0040 + +#define APM_STATE_DISABLE 0x0000 +#define APM_STATE_ENABLE 0x0001 + +#define APM_STATE_DISENGAGE 0x0000 +#define APM_STATE_ENGAGE 0x0001 + +/* + * Events (results of Get PM Event) + */ +#define APM_SYS_STANDBY 0x0001 +#define APM_SYS_SUSPEND 0x0002 +#define APM_NORMAL_RESUME 0x0003 +#define APM_CRITICAL_RESUME 0x0004 +#define APM_LOW_BATTERY 0x0005 +#define APM_POWER_STATUS_CHANGE 0x0006 +#define APM_UPDATE_TIME 0x0007 +#define APM_CRITICAL_SUSPEND 0x0008 +#define APM_USER_STANDBY 0x0009 +#define APM_USER_SUSPEND 0x000a +#define APM_STANDBY_RESUME 0x000b +#define APM_CAPABILITY_CHANGE 0x000c +#define APM_USER_HIBERNATION 0x000d +#define APM_HIBERNATION_RESUME 0x000e + +/* + * Error codes + */ +#define APM_SUCCESS 0x00 +#define APM_DISABLED 0x01 +#define APM_CONNECTED 0x02 +#define APM_NOT_CONNECTED 0x03 +#define APM_16_CONNECTED 0x05 +#define APM_16_UNSUPPORTED 0x06 +#define APM_32_CONNECTED 0x07 +#define APM_32_UNSUPPORTED 0x08 +#define APM_BAD_DEVICE 0x09 +#define APM_BAD_PARAM 0x0a +#define APM_NOT_ENGAGED 0x0b +#define APM_BAD_FUNCTION 0x0c +#define APM_RESUME_DISABLED 0x0d +#define APM_NO_ERROR 0x53 +#define APM_BAD_STATE 0x60 +#define APM_NO_EVENTS 0x80 +#define APM_NOT_PRESENT 0x86 + +/* + * APM Device IDs + */ +#define APM_DEVICE_BIOS 0x0000 +#define APM_DEVICE_ALL 0x0001 +#define APM_DEVICE_DISPLAY 0x0100 +#define APM_DEVICE_STORAGE 0x0200 +#define APM_DEVICE_PARALLEL 0x0300 +#define APM_DEVICE_SERIAL 0x0400 +#define APM_DEVICE_NETWORK 0x0500 +#define APM_DEVICE_PCMCIA 0x0600 +#define APM_DEVICE_BATTERY 0x8000 +#define APM_DEVICE_OEM 0xe000 +#define APM_DEVICE_OLD_ALL 0xffff +#define APM_DEVICE_CLASS 0x00ff +#define APM_DEVICE_MASK 0xff00 + + +/* + * Battery status + */ +#define APM_MAX_BATTERIES 2 + +/* + * APM defined capability bit flags + */ +#define APM_CAP_GLOBAL_STANDBY 0x0001 +#define APM_CAP_GLOBAL_SUSPEND 0x0002 +#define APM_CAP_RESUME_STANDBY_TIMER 0x0004 /* Timer resume from standby */ +#define APM_CAP_RESUME_SUSPEND_TIMER 0x0008 /* Timer resume from suspend */ +#define APM_CAP_RESUME_STANDBY_RING 0x0010 /* Resume on Ring fr standby */ +#define APM_CAP_RESUME_SUSPEND_RING 0x0020 /* Resume on Ring fr suspend */ +#define APM_CAP_RESUME_STANDBY_PCMCIA 0x0040 /* Resume on PCMCIA Ring */ +#define APM_CAP_RESUME_SUSPEND_PCMCIA 0x0080 /* Resume on PCMCIA Ring */ + +/* + * ioctl operations + */ +#include + +#define APM_IOC_STANDBY _IO('A', 1) +#define APM_IOC_SUSPEND _IO('A', 2) + +#endif /* _UAPI_LINUX_APM_H */ diff --git a/t/tree/include/uapi/linux/eventpoll.h b/t/tree/include/uapi/linux/eventpoll.h new file mode 100644 index 00000000..8a3432d0 --- /dev/null +++ b/t/tree/include/uapi/linux/eventpoll.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * include/linux/eventpoll.h ( Efficient event polling implementation ) + * Copyright (C) 2001,...,2006 Davide Libenzi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Davide Libenzi + * + */ + +#ifndef _UAPI_LINUX_EVENTPOLL_H +#define _UAPI_LINUX_EVENTPOLL_H + +/* For O_CLOEXEC */ +#include +#include + +/* Flags for epoll_create1. */ +#define EPOLL_CLOEXEC O_CLOEXEC + +/* Valid opcodes to issue to sys_epoll_ctl() */ +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 + +/* Epoll event masks */ +#define EPOLLIN (__force __poll_t)0x00000001 +#define EPOLLPRI (__force __poll_t)0x00000002 +#define EPOLLOUT (__force __poll_t)0x00000004 +#define EPOLLERR (__force __poll_t)0x00000008 +#define EPOLLHUP (__force __poll_t)0x00000010 +#define EPOLLNVAL (__force __poll_t)0x00000020 +#define EPOLLRDNORM (__force __poll_t)0x00000040 +#define EPOLLRDBAND (__force __poll_t)0x00000080 +#define EPOLLWRNORM (__force __poll_t)0x00000100 +#define EPOLLWRBAND (__force __poll_t)0x00000200 +#define EPOLLMSG (__force __poll_t)0x00000400 +#define EPOLLRDHUP (__force __poll_t)0x00002000 + +/* Set exclusive wakeup mode for the target file descriptor */ +#define EPOLLEXCLUSIVE ((__force __poll_t)(1U << 28)) + +/* + * Request the handling of system wakeup events so as to prevent system suspends + * from happening while those events are being processed. + * + * Assuming neither EPOLLET nor EPOLLONESHOT is set, system suspends will not be + * re-allowed until epoll_wait is called again after consuming the wakeup + * event(s). + * + * Requires CAP_BLOCK_SUSPEND + */ +#define EPOLLWAKEUP ((__force __poll_t)(1U << 29)) + +/* Set the One Shot behaviour for the target file descriptor */ +#define EPOLLONESHOT ((__force __poll_t)(1U << 30)) + +/* Set the Edge Triggered behaviour for the target file descriptor */ +#define EPOLLET ((__force __poll_t)(1U << 31)) + +/* + * On x86-64 make the 64bit structure have the same alignment as the + * 32bit structure. This makes 32bit emulation easier. + * + * UML/x86_64 needs the same packing as x86_64 + */ +#ifdef __x86_64__ +#define EPOLL_PACKED __attribute__((packed)) +#else +#define EPOLL_PACKED +#endif + +struct epoll_event { + __poll_t events; + __u64 data; +} EPOLL_PACKED; + +#ifdef CONFIG_PM_SLEEP +static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev) +{ + if ((epev->events & EPOLLWAKEUP) && !capable(CAP_BLOCK_SUSPEND)) + epev->events &= ~EPOLLWAKEUP; +} +#else +static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev) +{ + epev->events &= ~EPOLLWAKEUP; +} +#endif +#endif /* _UAPI_LINUX_EVENTPOLL_H */ diff --git a/t/tree/include/uapi/linux/i2c.h b/t/tree/include/uapi/linux/i2c.h new file mode 100644 index 00000000..f71a1751 --- /dev/null +++ b/t/tree/include/uapi/linux/i2c.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 Simon G. Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and + Frodo Looijaard */ + +#ifndef _UAPI_LINUX_I2C_H +#define _UAPI_LINUX_I2C_H + +#include + +/** + * struct i2c_msg - an I2C transaction segment beginning with START + * @addr: Slave address, either seven or ten bits. When this is a ten + * bit address, I2C_M_TEN must be set in @flags and the adapter + * must support I2C_FUNC_10BIT_ADDR. + * @flags: I2C_M_RD is handled by all adapters. No other flags may be + * provided unless the adapter exported the relevant I2C_FUNC_* + * flags through i2c_check_functionality(). + * @len: Number of data bytes in @buf being read from or written to the + * I2C slave address. For read transactions where I2C_M_RECV_LEN + * is set, the caller guarantees that this buffer can hold up to + * 32 bytes in addition to the initial length byte sent by the + * slave (plus, if used, the SMBus PEC); and this value will be + * incremented by the number of block data bytes received. + * @buf: The buffer into which data is read, or from which it's written. + * + * An i2c_msg is the low level representation of one segment of an I2C + * transaction. It is visible to drivers in the @i2c_transfer() procedure, + * to userspace from i2c-dev, and to I2C adapter drivers through the + * @i2c_adapter.@master_xfer() method. + * + * Except when I2C "protocol mangling" is used, all I2C adapters implement + * the standard rules for I2C transactions. Each transaction begins with a + * START. That is followed by the slave address, and a bit encoding read + * versus write. Then follow all the data bytes, possibly including a byte + * with SMBus PEC. The transfer terminates with a NAK, or when all those + * bytes have been transferred and ACKed. If this is the last message in a + * group, it is followed by a STOP. Otherwise it is followed by the next + * @i2c_msg transaction segment, beginning with a (repeated) START. + * + * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then + * passing certain @flags may have changed those standard protocol behaviors. + * Those flags are only for use with broken/nonconforming slaves, and with + * adapters which are known to support the specific mangling options they + * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). + */ +struct i2c_msg { + __u16 addr; /* slave address */ + __u16 flags; +#define I2C_M_RD 0x0001 /* read data, from slave to master */ + /* I2C_M_RD is guaranteed to be 0x0001! */ +#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ +#define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */ + /* makes only sense in kernelspace */ + /* userspace buffers are copied anyway */ +#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ +#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ +#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ + __u16 len; /* msg length */ + __u8 *buf; /* pointer to msg data */ +}; + +/* To determine what functionality is present */ + +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */ +#define I2C_FUNC_SMBUS_PEC 0x00000008 +#define I2C_FUNC_NOSTART 0x00000010 /* I2C_M_NOSTART */ +#define I2C_FUNC_SLAVE 0x00000020 +#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ +#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 + +#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE) +#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA) +#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA) +#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) +#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) + +#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK | \ + I2C_FUNC_SMBUS_PEC) + +/* + * Data for SMBus Messages + */ +#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ + /* and one more for user-space compatibility */ +}; + +/* i2c_smbus_xfer read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 +#define I2C_SMBUS_I2C_BLOCK_BROKEN 6 +#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ +#define I2C_SMBUS_I2C_BLOCK_DATA 8 + +#endif /* _UAPI_LINUX_I2C_H */ diff --git a/t/tree/include/uapi/linux/rseq.h b/t/tree/include/uapi/linux/rseq.h new file mode 100644 index 00000000..9a402fdb --- /dev/null +++ b/t/tree/include/uapi/linux/rseq.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_RSEQ_H +#define _UAPI_LINUX_RSEQ_H + +/* + * linux/rseq.h + * + * Restartable sequences system call API + * + * Copyright (c) 2015-2018 Mathieu Desnoyers + */ + +#include +#include + +enum rseq_cpu_id_state { + RSEQ_CPU_ID_UNINITIALIZED = -1, + RSEQ_CPU_ID_REGISTRATION_FAILED = -2, +}; + +enum rseq_flags { + RSEQ_FLAG_UNREGISTER = (1 << 0), +}; + +enum rseq_cs_flags_bit { + RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT = 0, + RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT = 1, + RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, +}; + +enum rseq_cs_flags { + RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT = + (1U << RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT), + RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL = + (1U << RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT), + RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE = + (1U << RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT), +}; + +/* + * struct rseq_cs is aligned on 4 * 8 bytes to ensure it is always + * contained within a single cache-line. It is usually declared as + * link-time constant data. + */ +struct rseq_cs { + /* Version of this structure. */ + __u32 version; + /* enum rseq_cs_flags */ + __u32 flags; + __u64 start_ip; + /* Offset from start_ip. */ + __u64 post_commit_offset; + __u64 abort_ip; +} __attribute__((aligned(4 * sizeof(__u64)))); + +/* + * struct rseq is aligned on 4 * 8 bytes to ensure it is always + * contained within a single cache-line. + * + * A single struct rseq per thread is allowed. + */ +struct rseq { + /* + * Restartable sequences cpu_id_start field. Updated by the + * kernel. Read by user-space with single-copy atomicity + * semantics. This field should only be read by the thread which + * registered this data structure. Aligned on 32-bit. Always + * contains a value in the range of possible CPUs, although the + * value may not be the actual current CPU (e.g. if rseq is not + * initialized). This CPU number value should always be compared + * against the value of the cpu_id field before performing a rseq + * commit or returning a value read from a data structure indexed + * using the cpu_id_start value. + */ + __u32 cpu_id_start; + /* + * Restartable sequences cpu_id field. Updated by the kernel. + * Read by user-space with single-copy atomicity semantics. This + * field should only be read by the thread which registered this + * data structure. Aligned on 32-bit. Values + * RSEQ_CPU_ID_UNINITIALIZED and RSEQ_CPU_ID_REGISTRATION_FAILED + * have a special semantic: the former means "rseq uninitialized", + * and latter means "rseq initialization failed". This value is + * meant to be read within rseq critical sections and compared + * with the cpu_id_start value previously read, before performing + * the commit instruction, or read and compared with the + * cpu_id_start value before returning a value loaded from a data + * structure indexed using the cpu_id_start value. + */ + __u32 cpu_id; + /* + * Restartable sequences rseq_cs field. + * + * Contains NULL when no critical section is active for the current + * thread, or holds a pointer to the currently active struct rseq_cs. + * + * Updated by user-space, which sets the address of the currently + * active rseq_cs at the beginning of assembly instruction sequence + * block, and set to NULL by the kernel when it restarts an assembly + * instruction sequence block, as well as when the kernel detects that + * it is preempting or delivering a signal outside of the range + * targeted by the rseq_cs. Also needs to be set to NULL by user-space + * before reclaiming memory that contains the targeted struct rseq_cs. + * + * Read and set by the kernel. Set by user-space with single-copy + * atomicity semantics. This field should only be updated by the + * thread which registered this data structure. Aligned on 64-bit. + */ + union { + __u64 ptr64; +#ifdef __LP64__ + __u64 ptr; +#else + struct { +#if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(__BIG_ENDIAN) + __u32 padding; /* Initialized to zero. */ + __u32 ptr32; +#else /* LITTLE */ + __u32 ptr32; + __u32 padding; /* Initialized to zero. */ +#endif /* ENDIAN */ + } ptr; +#endif + } rseq_cs; + + /* + * Restartable sequences flags field. + * + * This field should only be updated by the thread which + * registered this data structure. Read by the kernel. + * Mainly used for single-stepping through rseq critical sections + * with debuggers. + * + * - RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT + * Inhibit instruction sequence block restart on preemption + * for this thread. + * - RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL + * Inhibit instruction sequence block restart on signal + * delivery for this thread. + * - RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE + * Inhibit instruction sequence block restart on migration for + * this thread. + */ + __u32 flags; +} __attribute__((aligned(4 * sizeof(__u64)))); + +#endif /* _UAPI_LINUX_RSEQ_H */ From fa7243638d8814c3a075b180a600f84c871844b4 Mon Sep 17 00:00:00 2001 From: Christopher White Date: Tue, 21 Jan 2020 09:36:10 -0500 Subject: [PATCH 016/529] Add automated test of basic functions - Added test helpers in t/TestHelpers.pm - Added basic test of update.py and query.py in t/100-basic.t - Updated README: - Added automated-testing section - Formatted identifiers (i.e., added backticks) --- README.md | 60 +++++++++---- t/.gitignore | 2 + t/100-basic.t | 145 ++++++++++++++++++++++++++++++ t/TestHelpers.pm | 224 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 413 insertions(+), 18 deletions(-) create mode 100644 t/.gitignore create mode 100644 t/100-basic.t create mode 100644 t/TestHelpers.pm diff --git a/README.md b/README.md index da7e567e..40dc1e69 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # The Elixir Cross Referencer - Elixir is a source code cross-referencer inspired by [LXR](https://en.wikipedia.org/wiki/LXR_Cross_Referencer). It's written in Python and its main purpose is to index every release of a C or C++ @@ -13,14 +12,16 @@ duplicating work and data. It has a straightforward data structure You can see it in action on https://elixir.bootlin.com/ +Note: this documentation applies to version 1.0 of Elixir. + # Requirements * Python >= 3.5 * The Jinja2 and Pygments (>= 2.2) Python libraries * Berkeley DB (and its Python binding) * Exuberant Ctags -* Perl (for non-greedy regexes) -* Falcon and mod_wsgi (for the REST api) +* Perl (for non-greedy regexes and automated testing) +* Falcon and `mod_wsgi` (for the REST api) # Installation @@ -63,8 +64,8 @@ yum install python36-jinja2 python36-pygments python36-bsddb3 python3-falcon glo sudo apt install python3 python3-jinja2 python3-pygments python3-bsddb3 python3-falcon exuberant-ctags perl git apache2 libapache2-mod-wsgi-py3 ``` -To enable the REST api, follow the installation instructions on [mod_wsgi](https://github.com/GrahamDumpleton/mod_wsgi) -and connect it to the apache installation as detailed in https://github.com/GrahamDumpleton/mod_wsgi#connecting-into-apache-installation +To enable the REST api, follow the installation instructions on [`mod_wsgi`](https://github.com/GrahamDumpleton/mod_wsgi) +and connect it to the apache installation as detailed in . To know which packages to install, you can also read the Docker files in the `docker/` directory to know what packages Elixir needs in your favorite distribution. @@ -87,8 +88,8 @@ mkdir -p /path/elixir-data/linux/data Two environment variables are used to tell Elixir where to find the project's local git repository and its databases: -* LXR_REPO_DIR (the git repository directory for your project) -* LXR_DATA_DIR (the database directory for your project) +* `LXR_REPO_DIR` (the git repository directory for your project) +* `LXR_DATA_DIR` (the database directory for your project) Now open `/etc/profile` and append the following content. @@ -150,16 +151,16 @@ server. Since it includes support for indexing multiple projects, it expects a different variable (`LXR_PROJ_DIR`) which points to a directory with a specific structure: -* - * - * data - * repo - * - * data - * repo - * - * data - * repo +* `` + * `` + * `data` + * `repo` + * `` + * `data` + * `repo` + * `` + * `data` + * `repo` It will then generate the other two variables upon calling the query command. @@ -415,4 +416,27 @@ The response body is of the following structure: } ``` -Note: this documentation applies to version 1.0 of Elixir. +# Automated testing + +Elixir includes a simple test suite in `t/`. To run it, +from the top-level Elixir directory, run: + + prove + +The test suite uses code extracted from Linux v5.4 in `t/tree`. + +## Licensing of code in `t/tree` + +The copied code is licensed as described in the [COPYING] file included with +Linux. All the files copied carry SPDX license identifiers of `GPL-2.0+` or +`GPL-2.0-or-later`. Per [GNU's compatibility table], GPL 2.0+ code can be used +under GPLv3 provided the combination is under GPLv3. Moreover, [GNU's overview +of AGPLv3] indicates that its terms "effectively consist of the terms of GPLv3" +plus the network-use paragraph. Therefore, the developers have a good-faith +belief that licensing these files under AGPLv3 is authorized. (See also [this +issue comment] for another example of a similar situation.) + +[COPYING]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/COPYING +[GNU's compatibility table]: https://www.gnu.org/licenses/gpl-faq.en.html#AllCompatibility +[GNU's overview of AGPLv3]: https://www.gnu.org/licenses/license-list.en.html#AGPLv3.0 +[this issue comment]: https://github.com/Freemius/wordpress-sdk/issues/166#issuecomment-310561976 diff --git a/t/.gitignore b/t/.gitignore new file mode 100644 index 00000000..439ae2f5 --- /dev/null +++ b/t/.gitignore @@ -0,0 +1,2 @@ +# Where we put the test database used by 100-basic.t +/db/ diff --git a/t/100-basic.t b/t/100-basic.t new file mode 100644 index 00000000..ef1a66b9 --- /dev/null +++ b/t/100-basic.t @@ -0,0 +1,145 @@ +#!/usr/bin/env perl +# 100-basic.t: Test basic elixir functions against the files in tree/ . +# +# Copyright (c) 2020 D3 Engineering, LLC. +# By Christopher White, . +# +# Elixir is free software; you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Elixir is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Elixir. If not, see . +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file uses core Perl modules only. + +use strict; +use warnings; +use autodie; # note: still need to check system() calls manually + +use FindBin '$Bin'; +use lib $Bin; + +use Cwd qw(abs_path); +use File::Path qw(remove_tree); +use File::Spec; +use File::Temp 0.14 qw(tempdir); + +use Test::More; + +use TestHelpers; + +# =========================================================================== +# Main + +# Set up +my $tree_src_dir = sibling_abs_path('tree'); +my $db_dir = sibling_abs_path('db'); # the db dir is .gitignored + +{ # Remove any existing DB dir + my $ignore; + remove_tree($db_dir, {error => \$ignore}); +} + +# Check programs +my $script_sh = find_program('script.sh'); +my $update_py = find_program('update.py'); +my $query_py = find_program('query.py'); + +ok_or_die( (-f $script_sh && -r _ && -x _), 'script.sh executable', + "Could not find executable script.sh at $script_sh"); +ok_or_die( (-f $update_py && -r _ && -x _), 'update.py executable', + "Could not find executable update.py at $update_py"); +ok_or_die( (-f $query_py && -r _ && -x _), 'query.py executable', + "Could not find executable query.py at $query_py"); + +# Copy tree/ into a temporary Git repository, since script.sh requires +# it be run in a Git repo. + +my $tempdir = tempdir(CLEANUP => 1); +my $tempdir_path = abs_path($tempdir); +diag "Using temporary directory $tempdir_path"; +run_program('bash', '-c', "cd \"$tempdir\" && git init") or die("git init failed"); + +run_program('bash', '-c', "tar cf - -C \"$tree_src_dir\" . | tar xf - -C \"$tempdir\"") + or die("Could not copy files into $tempdir"); + +my @gitdir = ('-C', $tempdir_path); + +run_program('git', @gitdir, 'add', '.') or die("git add failed"); +run_program('git', @gitdir, 'commit', '-am', 'Initial commit') + or die("git commit failed"); +run_program('git', @gitdir, 'tag', 'v5.4') or die("git tag failed"); + +$ENV{LXR_REPO_DIR} = $tempdir; +$ENV{LXR_DATA_DIR} = $db_dir; + +# Check for tags in `script.sh list-tags`, as a sanity check before +# building the test DB +my @tags = `$script_sh list-tags`; +die("Could not list tags: $! ($?)") if $?; +ok_or_die( @tags == 1, 'One tag present', "Not one tag (@{[scalar @tags]})"); +ok_or_die( $tags[0] =~ /^v5.4$/, 'Found the correct tag', 'Not the tag we expected'); + +# Make the new database +ok_or_die( mkdir($db_dir), "Created $db_dir", + "Could not create $db_dir"); + +ok_or_die( run_program($update_py), 'update.py succeeded', + 'Could not create database'); + +ok_or_die( -d $db_dir, 'database dir exists', + "Database dir $db_dir not present"); + +# Make sure the database has the files we expect +ok( (-r File::Spec->catfile($db_dir, $_)), "$_ exists" ) + foreach qw(blobs.db definitions.db filenames.db hashes.db references.db + variables.db versions.db); + +# Spot-check some identifiers + +run_produces_ok('ident query (nonexistent)', + [$query_py, qw(v5.4 ident SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH)], + [qr{^Symbol Definitions:}, qr{^Symbol References:}, qr{^\s*$}], + 1); + +run_produces_ok('ident query (existent)', + [$query_py, qw(v5.4 ident i2c_acpi_notify)], + [qr{^Symbol Definitions:}, qr{^Symbol References:}, + qr{drivers/i2c/i2c-core-acpi\.c.+\b402\b.+\bfunction\b}, # def + qr{drivers/i2c/i2c-core-acpi\.c.+\b402,439} # refs + ], + 1); + +# Spot-check some files + +run_produces_ok('file query (nonexistent)', + [$query_py, qw(v5.4 file /SOME_NONEXISTENT_FILENAME_XYZZY_PLUGH)], + [{not => qr{\S}}]); + +run_produces_ok('file query (existent), .h', + [$query_py, qw(v5.4 file /drivers/i2c/i2c-dev.c)], + [qr{\S}], + 1); + +run_produces_ok('file query (existent), .c', + [$query_py, qw(v5.4 file /drivers/i2c/i2c-dev.c)], + [qr{i2c-dev\.c}, qr{\bVogl\b}], + 1); + +run_produces_ok('file query (existent), .h', + [$query_py, qw(v5.4 file /drivers/i2c/i2c-core.h)], + [qr{i2c-core\.h}, qr{\bWe\b}], + 1); + +#system('bash'); # Uncomment this if you want to interact with the test repo + +done_testing; diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm new file mode 100644 index 00000000..4cd06f18 --- /dev/null +++ b/t/TestHelpers.pm @@ -0,0 +1,224 @@ +#!/usr/bin/env perl +# TestHelpers.pm: Common routines for use in tests. +# See license information at end of file. +# +# For a cleaner view of the documentation, run +# perldoc TestHelpers.pm +# (on Ubuntu, you may need to install the perl-doc package first.) +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file uses core Perl modules only. + +=head1 NAME + +TestHelpers - Common routines for use in tests + +=head1 SYNOPSIS + +C, and all the functions below will be exported. + +=head1 FUNCTIONS + +=cut + +package TestHelpers; + +use strict; +use warnings; +use autodie; # note: still need to check system() calls manually + +use File::Spec; +use FindBin; +use IO::Select; +use IPC::Open3; +use Symbol; + +use Test::More; + +# Automatically export all the functions below +use parent 'Exporter'; +our @EXPORT; +BEGIN { @EXPORT = qw(sibling_abs_path find_program run_program ok_or_die + run_produces_ok); } + +# =========================================================================== + +=head2 sibling_abs_path + +Return the absolute path of a file or directory in the same directory as +this file. Usage: + + $path = sibling_abs_path('name'); + +=cut + +sub sibling_abs_path { + return File::Spec->rel2abs(File::Spec->catfile($FindBin::Bin, @_)); +} + +=head2 find_program + +Looks for a program in the parent directory of this script. +Usage: + + $path = find_program('program name') + +=cut + +sub find_program { + my $program = shift; + + my ($vol, $directories, $file) = File::Spec->splitpath($FindBin::Bin, 1); # 1 => is a dir + + # Go up to the parent of the directory holding this file + my @dirs = File::Spec->splitdir($directories); + die "Cannot run from the root directory" unless @dirs >= 2; + pop @dirs; + $directories = File::Spec->catdir(@dirs); + + return File::Spec->catpath($vol, $directories, $program); +} #find_program() + +=head2 run_program + +Print a command, then run it. Returns true if system() and the command +succeed, false otherwise. Usage: + + $ok = run_program('program', 'arg1', ...) + +=cut + +sub run_program { + diag "Running @_"; + my $status = system(@_); + if ($status == -1) { + diag "failed to execute $_[0]: $!"; + } + elsif ($status & 127) { + diag sprintf "$_[0] died with signal %d, %s coredump\n", + ($status & 127), ($status & 128) ? 'with' : 'without'; + } + else { + diag sprintf "$_[0] exited with value %d\n", $status >> 8; + } + + return($status == 0); +} #run_program() + +=head2 ok_or_die + +Run a test, but die if it fails. Usage: + + ok_or_die( , 'description', 'what to print if it dies' ) + +=cut + +sub ok_or_die { + my ($cond, $msg, $err_msg) = @_; + my $line = (caller)[2]; + my $retval = eval < $mustSucceed) + +The test passes if each regex in C<@expected_regexes> matches at least one +line in the output of C<@program_and_args>, and if each C<< { not => regex } >> +in C<@expected_regexes> is NOT found in that output. +If C<$mustSucceed> is true, also tests for exit status 0 and empty stderr. + +=cut + +sub run_produces_ok { + my ($desc, $lrProgram, $lrRegexes, $mustSucceed) = @_; + + # Run program and capture stdout and stderr + my ($in , $out, $err); # Filehandles + $err = Symbol::gensym; + diag "Running @$lrProgram"; + my $pid = open3($in, $out, $err, @$lrProgram); + + my (@outlines, @errlines); # Captured output + my $s = IO::Select->new; + $s->add($out); + $s->add($err); + + while(my @ready = $s->can_read) { + for my $fh (@ready) { + + if(eof($fh)) { + $s->remove($fh); + next; + } + + if($fh == $out) { + push @outlines, scalar readline $fh; + } else { + push @errlines, scalar readline $fh; + } + } + } + + waitpid $pid, 0; + my $exit_status = $? >> 8; + + # Basic checks + if($mustSucceed) { + cmp_ok($exit_status, '==', 0, "$desc: exit status 0"); + cmp_ok(@errlines, '==', 0, "$desc: stderr empty"); + } + + # Check regexes + for my $entry (@$lrRegexes) { + + if(ref $entry eq 'Regexp') { + ok( (grep { m{$entry} } @outlines), "$desc: output includes $entry" ); + + } elsif(ref $entry eq 'HASH' && ref $entry->{not} eq 'Regexp') { + my $re = $entry->{not}; + ok( !(grep { m{$re} } @outlines), "$desc: output excludes $re" ); + + } else { + die "Invalid entry $entry"; + } + } #foreach $entry + +} #run_produces_ok() + +1; +__END__ + + +=head1 AUTHOR + +Christopher White, C<< >> + +=head1 COPYRIGHT + +Copyright (c) 2020 D3 Engineering, LLC. + +Elixir is free software; you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Elixir is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with Elixir. If not, see . + +=cut From 97ef91036a968a2087c0232e71138d8d8d5eb6dc Mon Sep 17 00:00:00 2001 From: pcworld <0188801@gmail.com> Date: Wed, 26 Feb 2020 22:49:57 +0100 Subject: [PATCH 017/529] Prepend filename or identifier in HTML Display filenames and identifiers at the *beginning* of the <title>. This way, they are better visible in modern browsers' tabs as they usually cut off the title for space reasons. This also fixes handling of the root path. Examples of changed titles: "Linux source code: (v5.5.6) - Bootlin" becomes "Linux source code (v5.5.6) - Bootlin" "Linux source code: arch (v5.5.6) - Bootlin" becomes "arch - Linux source code (v5.5.6) - Bootlin" "Linux source code: arch/um/Makefile (v5.5.6) - Bootlin" becomes "Makefile - arch/um/Makefile - Linux source code (v5.5.6) - Bootlin" --- http/web.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/http/web.py b/http/web.py index 4fee7713..30ca7fcc 100755 --- a/http/web.py +++ b/http/web.py @@ -162,19 +162,28 @@ def print(arg, end='\n'): data['versions'] = v +title_suffix = project.capitalize()+' source code ('+tag+') - Bootlin' + if mode == 'source': - p2 = '' - p3 = path.split('/') [1:] + path_split = path.split('/')[1:] + path_temp = '' links = [] - for p in p3: - p2 += '/'+p - links.append('<a href="'+version+'/source'+p2+'">'+p+'</a>') + for p in path_split: + path_temp += '/'+p + links.append('<a href="'+version+'/source'+path_temp+'">'+p+'</a>') if links: data['breadcrumb'] += '/'.join(links) data['ident'] = ident - data['title'] = project.capitalize()+' source code: '+path[1:]+' ('+tag+') - Bootlin' + # Create titles like this: + # root path: "Linux source code (v5.5.6) - Bootlin" + # first level path: "arch - Linux source code (v5.5.6) - Bootlin" + # deeper paths: "Makefile - arch/um/Makefile - Linux source code (v5.5.6) - Bootlin" + data['title'] = ('' if path == '' + else path_split[0]+' - ' if len(path_split) == 1 + else path_split[-1]+' - '+'/'.join(path_split)+' - ') \ + +title_suffix lines = ['null - -'] @@ -277,7 +286,7 @@ def print(arg, end='\n'): elif mode == 'ident': - data['title'] = project.capitalize()+' source code: '+ident+' identifier ('+tag+') - Bootlin' + data['title'] = ident+' identifier - '+title_suffix symbol_definitions, symbol_references = query('ident', tag, ident) From e9d970ced1a5290e8f0e98437b6f4b0d4ad3c1a0 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker <michael.opdenacker@bootlin.com> Date: Fri, 6 Mar 2020 12:04:39 +0100 Subject: [PATCH 018/529] Fix handling of malformed URLs - Otherwise, we end up with a wrong path variable that causes an internal server error Signed-off-by: Michael Opdenacker <michael.opdenacker@bootlin.com> --- http/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/web.py b/http/web.py index 30ca7fcc..fee22031 100755 --- a/http/web.py +++ b/http/web.py @@ -73,7 +73,7 @@ def print(arg, end='\n'): else: mode = 'source' if not search('^[A-Za-z0-9_/.,+-]*$', path): - path = 'INVALID' + status = 400 url = 'source'+path elif cmd == 'ident': From 0a96ceef9d7b078fcb986eb62adbd318c90952a0 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir <carmeli.tamir@gmail.com> Date: Sat, 7 Mar 2020 03:38:19 -0500 Subject: [PATCH 019/529] Return empty lists when no identifier found --- api/api.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/api/api.py b/api/api.py index 0edd0779..1f31e730 100755 --- a/api/api.py +++ b/api/api.py @@ -27,16 +27,12 @@ def on_get(self, req, resp, project, ident): version = query('latest') symbol_definitions, symbol_references = query('ident', version, ident) - if len(symbol_definitions) or len(symbol_references): - resp.body = json.dumps( - { - 'definitions': [sym.__dict__ for sym in symbol_definitions], - 'references': [sym.__dict__ for sym in symbol_references] - }) - resp.status = falcon.HTTP_200 - else: - raise falcon.HTTPNotFound({'title': 'Requested identifier not found in {} project'.format(project)}) - + resp.body = json.dumps( + { + 'definitions': [sym.__dict__ for sym in symbol_definitions], + 'references': [sym.__dict__ for sym in symbol_references] + }) + resp.status = falcon.HTTP_200 application = falcon.API() From a1e8117afddf62eaec3aca5e867459c4693552f5 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir <carmeli.tamir@gmail.com> Date: Mon, 2 Mar 2020 14:51:56 -0500 Subject: [PATCH 020/529] Check test failure --- .travis.yml | 14 ++++++++++++++ data.py | 2 +- query.py | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..a17b3f03 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: python + +os: + - linux + +python: + - "3.6" + +before_install: + - sudo apt-get -y install libdb-dev exuberant-ctags + - pip install jinja2 pygments bsddb3 falcon + +script: + - prove \ No newline at end of file diff --git a/data.py b/data.py index 2e85ec04..e09ba5ca 100755 --- a/data.py +++ b/data.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # This file is part of Elixir, a source code cross-referencer. # diff --git a/query.py b/query.py index 4fc07de0..22452666 100755 --- a/query.py +++ b/query.py @@ -191,7 +191,7 @@ def query(cmd, *args): def cmd_ident(version, ident, **kwargs): symbol_definitions, symbol_references = query("ident", version, ident) - print("Symbol Definitions:") + print("Symbol Definitionss:") for symbol_definition in symbol_definitions: print(symbol_definition) From 1db4fb81999ecfac12a48f62453d319c9aa75582 Mon Sep 17 00:00:00 2001 From: Christopher White <cwhite@d3engineering.com> Date: Mon, 9 Mar 2020 16:29:57 -0400 Subject: [PATCH 021/529] Refactor common test code into class - Create new class t/TestEnvironment.pm. A TestEnvironment instance owns a temporary Git repo and a temporary Elixir database directory. - Update t/100-basic.t to use TestEnvironment. - Add constant MUST_SUCCEED so `run_produces_ok()` calls are more self-documenting --- t/100-basic.t | 55 +++------- t/TestClass.pm | 8 ++ t/TestEnvironment.pm | 238 +++++++++++++++++++++++++++++++++++++++++++ t/TestHelpers.pm | 20 +++- 4 files changed, 277 insertions(+), 44 deletions(-) create mode 100644 t/TestClass.pm create mode 100644 t/TestEnvironment.pm diff --git a/t/100-basic.t b/t/100-basic.t index ef1a66b9..5d837fe2 100644 --- a/t/100-basic.t +++ b/t/100-basic.t @@ -28,13 +28,11 @@ use autodie; # note: still need to check system() calls manually use FindBin '$Bin'; use lib $Bin; -use Cwd qw(abs_path); -use File::Path qw(remove_tree); use File::Spec; -use File::Temp 0.14 qw(tempdir); use Test::More; +use TestEnvironment; use TestHelpers; # =========================================================================== @@ -44,15 +42,12 @@ use TestHelpers; my $tree_src_dir = sibling_abs_path('tree'); my $db_dir = sibling_abs_path('db'); # the db dir is .gitignored -{ # Remove any existing DB dir - my $ignore; - remove_tree($db_dir, {error => \$ignore}); -} +my $tenv = TestEnvironment->new; # Check programs -my $script_sh = find_program('script.sh'); -my $update_py = find_program('update.py'); -my $query_py = find_program('query.py'); +my $script_sh = $tenv->script_sh; +my $update_py = $tenv->update_py; +my $query_py = $tenv->query_py; ok_or_die( (-f $script_sh && -r _ && -x _), 'script.sh executable', "Could not find executable script.sh at $script_sh"); @@ -61,26 +56,8 @@ ok_or_die( (-f $update_py && -r _ && -x _), 'update.py executable', ok_or_die( (-f $query_py && -r _ && -x _), 'query.py executable', "Could not find executable query.py at $query_py"); -# Copy tree/ into a temporary Git repository, since script.sh requires -# it be run in a Git repo. - -my $tempdir = tempdir(CLEANUP => 1); -my $tempdir_path = abs_path($tempdir); -diag "Using temporary directory $tempdir_path"; -run_program('bash', '-c', "cd \"$tempdir\" && git init") or die("git init failed"); - -run_program('bash', '-c', "tar cf - -C \"$tree_src_dir\" . | tar xf - -C \"$tempdir\"") - or die("Could not copy files into $tempdir"); - -my @gitdir = ('-C', $tempdir_path); - -run_program('git', @gitdir, 'add', '.') or die("git add failed"); -run_program('git', @gitdir, 'commit', '-am', 'Initial commit') - or die("git commit failed"); -run_program('git', @gitdir, 'tag', 'v5.4') or die("git tag failed"); - -$ENV{LXR_REPO_DIR} = $tempdir; -$ENV{LXR_DATA_DIR} = $db_dir; +$tenv->build_repo($tree_src_dir); +$tenv->update_env; # Set LXR_REPO_DIR # Check for tags in `script.sh list-tags`, as a sanity check before # building the test DB @@ -89,12 +66,8 @@ die("Could not list tags: $! ($?)") if $?; ok_or_die( @tags == 1, 'One tag present', "Not one tag (@{[scalar @tags]})"); ok_or_die( $tags[0] =~ /^v5.4$/, 'Found the correct tag', 'Not the tag we expected'); -# Make the new database -ok_or_die( mkdir($db_dir), "Created $db_dir", - "Could not create $db_dir"); - -ok_or_die( run_program($update_py), 'update.py succeeded', - 'Could not create database'); +$tenv->build_db($db_dir); +$tenv->update_env; # Set LXR_DATA_DIR ok_or_die( -d $db_dir, 'database dir exists', "Database dir $db_dir not present"); @@ -109,7 +82,7 @@ ok( (-r File::Spec->catfile($db_dir, $_)), "$_ exists" ) run_produces_ok('ident query (nonexistent)', [$query_py, qw(v5.4 ident SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, qr{^\s*$}], - 1); + MUST_SUCCEED); run_produces_ok('ident query (existent)', [$query_py, qw(v5.4 ident i2c_acpi_notify)], @@ -117,7 +90,7 @@ run_produces_ok('ident query (existent)', qr{drivers/i2c/i2c-core-acpi\.c.+\b402\b.+\bfunction\b}, # def qr{drivers/i2c/i2c-core-acpi\.c.+\b402,439} # refs ], - 1); + MUST_SUCCEED); # Spot-check some files @@ -128,17 +101,17 @@ run_produces_ok('file query (nonexistent)', run_produces_ok('file query (existent), .h', [$query_py, qw(v5.4 file /drivers/i2c/i2c-dev.c)], [qr{\S}], - 1); + MUST_SUCCEED); run_produces_ok('file query (existent), .c', [$query_py, qw(v5.4 file /drivers/i2c/i2c-dev.c)], [qr{i2c-dev\.c}, qr{\bVogl\b}], - 1); + MUST_SUCCEED); run_produces_ok('file query (existent), .h', [$query_py, qw(v5.4 file /drivers/i2c/i2c-core.h)], [qr{i2c-core\.h}, qr{\bWe\b}], - 1); + MUST_SUCCEED); #system('bash'); # Uncomment this if you want to interact with the test repo diff --git a/t/TestClass.pm b/t/TestClass.pm new file mode 100644 index 00000000..006e9488 --- /dev/null +++ b/t/TestClass.pm @@ -0,0 +1,8 @@ +# A minified OOP library - https://metacpan.org/pod/distribution/Mo/ReadMe.pod + +package TestClass; +# use Mo qw(build default is required import); +# The following line of code was produced from the previous line by +# Mo::Inline version 0.40 +no warnings;my$M=__PACKAGE__.'::';*{$M.Object::new}=sub{my$c=shift;my$s=bless{@_},$c;my%n=%{$c.'::'.':E'};map{$s->{$_}=$n{$_}->()if!exists$s->{$_}}keys%n;$s};*{$M.import}=sub{import warnings;$^H|=1538;my($P,%e,%o)=caller.'::';shift;eval"no Mo::$_",&{$M.$_.::e}($P,\%e,\%o,\@_)for@_;return if$e{M};%e=(extends,sub{eval"no $_[0]()";@{$P.ISA}=$_[0]},has,sub{my$n=shift;my$m=sub{$#_?$_[0]{$n}=$_[1]:$_[0]{$n}};@_=(default,@_)if!($#_%2);$m=$o{$_}->($m,$n,@_)for sort keys%o;*{$P.$n}=$m},%e,);*{$P.$_}=$e{$_}for keys%e;@{$P.ISA}=$M.Object};*{$M.'build::e'}=sub{my($P,$e)=@_;$e->{new}=sub{$c=shift;my$s=&{$M.Object::new}($c,@_);my@B;do{@B=($c.::BUILD,@B)}while($c)=@{$c.::ISA};exists&$_&&&$_($s)for@B;$s}};*{$M.'default::e'}=sub{my($P,$e,$o)=@_;$o->{default}=sub{my($m,$n,%a)=@_;exists$a{default}or return$m;my($d,$r)=$a{default};my$g='HASH'eq($r=ref$d)?sub{+{%$d}}:'ARRAY'eq$r?sub{[@$d]}:'CODE'eq$r?$d:sub{$d};my$i=exists$a{lazy}?$a{lazy}:!${$P.':N'};$i or ${$P.':E'}{$n}=$g and return$m;sub{$#_?$m->(@_):!exists$_[0]{$n}?$_[0]{$n}=$g->(@_):$m->(@_)}}};*{$M.'is::e'}=sub{my($P,$e,$o)=@_;$o->{is}=sub{my($m,$n,%a)=@_;$a{is}or return$m;sub{$#_&&$a{is}eq'ro'&&caller ne'Mo::coerce'?die$n.' is ro':$m->(@_)}}};*{$M.'required::e'}=sub{my($P,$e,$o)=@_;$o->{required}=sub{my($m,$n,%a)=@_;if($a{required}){my$C=*{$P."new"}{CODE}||*{$M.Object::new}{CODE};no warnings 'redefine';*{$P."new"}=sub{my$s=$C->(@_);my%a=@_[1..$#_];die$n." required"if!exists$a{$n};$s}}$m}};my$i=\&import;*{$M.import}=sub{(@_==2 and not$_[1])?pop@_:@_==1?push@_,grep!/import/,@f:();goto&$i};@f=qw[build default is required import];use strict;use warnings; +1; diff --git a/t/TestEnvironment.pm b/t/TestEnvironment.pm new file mode 100644 index 00000000..3a9d8e7b --- /dev/null +++ b/t/TestEnvironment.pm @@ -0,0 +1,238 @@ +#!/usr/bin/env perl +# TestEnvironment.pm: A class representing an Elixir test environment. +# See license information at end of file. +# +# For a cleaner view of the documentation, run +# perldoc TestEnvironment.pm +# (on Ubuntu, you may need to install the perl-doc package first.) +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file uses only core Perl modules, and modules bundled with +# the Elixir distribution. + +=head1 NAME + +TestEnvironment - Class representing an Elixir test environment + +=head1 SYNOPSIS + + use TestEnvironment; + my $tenv = TestEnvironment->new; + $tenv->build_repo($source_path); # Make a git repo + $tenv->build_db($db_path); # Run update.py + $tenv->export_env; # Set $LXR_* environment vars + # Now run tests against the database in $db_path + +=cut + +package TestEnvironment; + +use TestClass; # Now we are a class +use autodie; # note: still need to check system() calls manually + +use Cwd qw(abs_path); +use File::Path qw(remove_tree); +use File::Spec; +use File::Temp 0.14 qw(tempdir); +use FindBin; +use IO::Select; +use IPC::Open3; +use Symbol; + +use Test::More; + +use TestHelpers; + +=head1 ATTRIBUTES + +=head2 lxr_data_dir + +C<$lxr_data_dir> is the value to use in the C<LXR_DATA_DIR> environment +variable. + +=head2 lxr_repo_dir + +C<$lxr_repo_dir> is the value to use in the C<LXR_REPO_DIR> environment +variable. + +=head2 script_sh + +The path to C<script.sh>. Assigned by default using +C<TestHelpers/find_program> if not specified. + +=head2 query_py + +As L</script_sh>, but for C<query.py>. + +=head2 update_py + +As L</script_sh>, but for C<update.py>. + +=cut + +has lxr_data_dir => (); +has lxr_repo_dir => (); +has script_sh => ( + default => sub { find_program('script.sh') } +); +has query_py => ( + default => sub { find_program('query.py') } +); +has update_py => ( + default => sub { find_program('update.py') } +); + +# Internal attributes + +# a variable representing the temporary repository directory. +# When this goes out of scope, the directory will be removed. +has _repo_dir_token => (); + +# a variable representing the temporary DB directory, if any. +# When this goes out of scope, the directory will be removed. +has _data_dir_token => (); + +=head1 MEMBER FUNCTIONS + +=head2 build_repo + +Create a Git repo and tag it. Usage: + + $tenv->build_repo($source_tree_dir); + +C<$source_tree_dir> is the directory holding the tree of source files +you want to index. + +Dies on error. On success, returns the instance, for chaining. + +=cut + +sub build_repo { + my ($self, $tree_src_dir) = @_; + die "Need a source dir" unless $tree_src_dir; + + my $tempdir = tempdir(CLEANUP => 1); + my $tempdir_path = abs_path($tempdir); + my @gitdir = ('-C', $tempdir_path); + + diag "Using temporary directory $tempdir_path"; + run_program('git', 'init', $tempdir_path) or die("git init failed"); + + run_program('bash', '-c', "tar cf - -C \"$tree_src_dir\" . | tar xf - -C \"$tempdir_path\"") + or die("Could not copy files into $tempdir_path"); + + run_program('git', @gitdir, 'add', '.') or die("git add failed"); + run_program('git', @gitdir, 'commit', '-am', 'Initial commit') + or die("git commit failed"); + run_program('git', @gitdir, 'tag', 'v5.4') or die("git tag failed"); + + # Save the results in the instance + $self->_repo_dir_token($tempdir); + $self->lxr_repo_dir($tempdir_path); + + return $self; +} #build_repo() + +=head2 build_db + +Build a test database for the repository. L</lxr_repo_dir> must be set +before calling this. Usage: + + $tenv->build_db([$db_dir]) + +C<$db_dir> is the directory where you want to put the database. If you do +not provide one, a temporary directory will be created. + +Dies on error. On success, returns the instance, for chaining. + +B<CAUTION>: This function will remove the contents of C<$db_dir> +unconditionally. + +=cut + +sub build_db { + my ($self, $db_dir) = @_; + die "No repo dir" unless $self->lxr_repo_dir; + + if($db_dir) { # Remove any existing DB dir + remove_tree($db_dir); + mkdir($db_dir) or die "Could not create fresh $db_dir"; + } + + # Create a temp DB dir if necessary + my $temp_db_dir; + unless($db_dir) { + $temp_db_dir = tempdir(CLEANUP => 1); + $db_dir = abs_path($temp_db_dir); + } + + local $ENV{LXR_REPO_DIR} = $self->lxr_repo_dir; + local $ENV{LXR_DATA_DIR} = $db_dir; + + run_program($self->update_py) + or die "Could not create database from $ENV{LXR_REPO_DIR} in $ENV{LXR_DATA_DIR}"; + + $self->_data_dir_token($temp_db_dir); + $self->lxr_data_dir($db_dir); + + return $self; +} #build_db() + +=head2 update_env + +Set the C<LXR_REPO_DIR> and C<LXR_DATA_DIR> environment variables. +Will not set a variable if the corresponding member does not have a value. + +Returns the instance, for chaining. + +=cut + +sub update_env { + my $self = shift; + $ENV{LXR_REPO_DIR} = $self->lxr_repo_dir if $self->lxr_repo_dir; + $ENV{LXR_DATA_DIR} = $self->lxr_data_dir if $self->lxr_data_dir; + return $self; +} #update_env() + +=head2 DESTROY + +Destructor. Called automatically. + +=cut + +sub DESTROY { + local($., $@, $!, $^E, $?); + my $self = shift; + + # Release the temporary directories + $self->_data_dir_token(undef); + $self->_repo_dir_token(undef); +} + +1; +__END__ + + +=head1 AUTHOR + +Christopher White, C<< <cwhite@d3engineering.com> >> + +=head1 COPYRIGHT + +Copyright (c) 2020 D3 Engineering, LLC. + +Elixir is free software; you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Elixir is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with Elixir. If not, see <http://www.gnu.org/licenses/>. + +=cut diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm index 4cd06f18..b920da19 100644 --- a/t/TestHelpers.pm +++ b/t/TestHelpers.pm @@ -18,8 +18,6 @@ TestHelpers - Common routines for use in tests C<use TestHelpers;>, and all the functions below will be exported. -=head1 FUNCTIONS - =cut package TestHelpers; @@ -28,7 +26,9 @@ use strict; use warnings; use autodie; # note: still need to check system() calls manually +use Cwd qw(abs_path); use File::Spec; +use File::Temp 0.14 qw(tempdir); use FindBin; use IO::Select; use IPC::Open3; @@ -40,10 +40,24 @@ use Test::More; use parent 'Exporter'; our @EXPORT; BEGIN { @EXPORT = qw(sibling_abs_path find_program run_program ok_or_die - run_produces_ok); } + run_produces_ok MUST_SUCCEED); } # =========================================================================== +=head1 CONSTANTS + +=head2 MUST_SUCCEED + +True. So that calls to L</run_produces_ok> will be more self-explanatory. + +=cut + +use constant MUST_SUCCEED => !!1; + +=head1 FUNCTIONS + +These are helper routines that generally perform specific tasks. + =head2 sibling_abs_path Return the absolute path of a file or directory in the same directory as From 116bae31488d3adb9a803d980d6c3bb290b47ae9 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir <carmeli.tamir@gmail.com> Date: Tue, 10 Mar 2020 14:58:51 -0400 Subject: [PATCH 022/529] Check test success --- query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/query.py b/query.py index 22452666..4fc07de0 100755 --- a/query.py +++ b/query.py @@ -191,7 +191,7 @@ def query(cmd, *args): def cmd_ident(version, ident, **kwargs): symbol_definitions, symbol_references = query("ident", version, ident) - print("Symbol Definitionss:") + print("Symbol Definitions:") for symbol_definition in symbol_definitions: print(symbol_definition) From eb42ab020f5053c3512756592762d807b2b051a7 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir <carmeli.tamir@gmail.com> Date: Thu, 12 Mar 2020 16:10:12 -0400 Subject: [PATCH 023/529] Added CI status --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 40dc1e69..6f49f3b0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.com/bootlin/elixir.svg?branch=master)](https://travis-ci.com/bootlin/elixir) + # The Elixir Cross Referencer Elixir is a source code cross-referencer inspired by From cdd8dc461589a57942081b001d2a29ee75efaa97 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir <carmeli.tamir@gmail.com> Date: Fri, 20 Mar 2020 12:53:03 -0400 Subject: [PATCH 024/529] Added API tests --- api/api.py | 19 +++++++++++-------- t/100-basic.t | 4 ++++ t/TestHelpers.pm | 9 +++++++-- t/api_test.py | 23 +++++++++++++++++++++++ 4 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 t/api_test.py diff --git a/api/api.py b/api/api.py index 1f31e730..1fc05349 100755 --- a/api/api.py +++ b/api/api.py @@ -6,16 +6,17 @@ ELIXIR_DIR = os.path.dirname(__file__) + '/..' def build_query(env, project): - basedir = env['LXR_PROJ_DIR'] - os.environ['LXR_DATA_DIR'] = basedir + '/' + project + '/data' - os.environ['LXR_REPO_DIR'] = basedir + '/' + project + '/repo' + if 'LXR_DATA_DIR' not in os.environ or 'LXR_REPO_DIR' not in os.environ: + basedir = env['LXR_PROJ_DIR'] + os.environ['LXR_DATA_DIR']= basedir + '/' + project + '/data' + os.environ['LXR_REPO_DIR'] = basedir + '/' + project + '/repo' import sys sys.path = [ ELIXIR_DIR ] + sys.path import query return query.query -class IdentResource: +class IdentGetter: def on_get(self, req, resp, project, ident): query = build_query(req.env, project) if 'version' in req.params: @@ -34,8 +35,10 @@ def on_get(self, req, resp, project, ident): }) resp.status = falcon.HTTP_200 -application = falcon.API() +def create_ident_getter(): + application = falcon.API() + idents = IdentGetter() + application.add_route('/ident/{project}/{ident}', idents) + return application -idents = IdentResource() - -application.add_route('/ident/{project}/{ident}', idents) +application = create_ident_getter() diff --git a/t/100-basic.t b/t/100-basic.t index 5d837fe2..86d9a86b 100644 --- a/t/100-basic.t +++ b/t/100-basic.t @@ -113,6 +113,10 @@ run_produces_ok('file query (existent), .h', [qr{i2c-core\.h}, qr{\bWe\b}], MUST_SUCCEED); +# Test the api using pytest. Prints the test results. + +run_produces_ok('api pytest suite', ["pytest", "-v"], [], MUST_SUCCEED, 1); + #system('bash'); # Uncomment this if you want to interact with the test repo done_testing; diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm index b920da19..28dfb32c 100644 --- a/t/TestHelpers.pm +++ b/t/TestHelpers.pm @@ -145,17 +145,18 @@ Run a program and check whether it produces expected output. Usage: run_produces_ok($desc, \@program_and_args, \@expected_regexes, - <optional> $mustSucceed) + <optional> $mustSucceed, <optional> $printOutput) The test passes if each regex in C<@expected_regexes> matches at least one line in the output of C<@program_and_args>, and if each C<< { not => regex } >> in C<@expected_regexes> is NOT found in that output. If C<$mustSucceed> is true, also tests for exit status 0 and empty stderr. +If C<$printOutput> is true, prints the output of C<@program_and_args>. =cut sub run_produces_ok { - my ($desc, $lrProgram, $lrRegexes, $mustSucceed) = @_; + my ($desc, $lrProgram, $lrRegexes, $mustSucceed, $printOutput) = @_; # Run program and capture stdout and stderr my ($in , $out, $err); # Filehandles @@ -187,6 +188,10 @@ sub run_produces_ok { waitpid $pid, 0; my $exit_status = $? >> 8; + if ($printOutput) { + diag "@outlines"; + } + # Basic checks if($mustSucceed) { cmp_ok($exit_status, '==', 0, "$desc: exit status 0"); diff --git a/t/api_test.py b/t/api_test.py new file mode 100644 index 00000000..a7fafb8e --- /dev/null +++ b/t/api_test.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import os +import sys + +from falcon import testing + +api_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..','api')) +sys.path.insert(0, api_dir) + +from api import create_ident_getter + +class APITest(testing.TestCase): + def setUp(self): + super(APITest, self).setUp() + + self.app = create_ident_getter() + +class TestMyApp(APITest): + def test_identifier_not_found(self): + result = self.simulate_get('/ident/tree/SOME_NONEXISTENT_IDENTIFIER', query_string="version=latest") + + self.assertEqual(result.status_code, 200) + self.assertEqual(result.json, {'definitions': [], 'references':[]}) \ No newline at end of file From f112ab08264aaa5d88b4be43bd52a319ae35c272 Mon Sep 17 00:00:00 2001 From: Chris White <cxwembedded@gmail.com> Date: Sat, 21 Mar 2020 08:58:43 -0400 Subject: [PATCH 025/529] Regularize whitespace; add editorconfig - Change the few files that use leading tabs into leading spaces. This way they match the other files in the repo. - Add an EditorConfig file so that supported editors will automatically use spaces instead of tabs. - (Extra) Add some common editor backup-file extensions to .gitignore. --- .editorconfig | 7 ++ .gitignore | 5 ++ README.md | 2 +- http/banner.css | 94 +++++++++++++-------------- script.sh | 6 +- templates/header.html | 70 ++++++++++---------- templates/layout.html | 120 +++++++++++++++++------------------ utils/index-all-repositories | 18 +++--- 8 files changed, 167 insertions(+), 155 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..7ac3e2c6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +# see https://editorconfig.org for the format of this file +root = true + +[*] +end_of_line = LF +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index a6bd427b..5ac14179 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ http/images http/*.html http/favicon.ico http/robots.txt + +# Editor and other backup files +*.swp +*~ +~* diff --git a/README.md b/README.md index 40dc1e69..1af967e4 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,7 @@ server.document-root = server_root + "/elixir/http" url.redirect = ( "^/$" => "/linux/latest/source" ) url.rewrite = ( "^/.*/(source|ident|search)" => "/web.py/$1") setenv.add-environment = ( "PYTHONIOENCODING" => "utf-8", - "LXR_PROJ_DIR" => "/path/to/elixir-data" ) + "LXR_PROJ_DIR" => "/path/to/elixir-data" ) ``` ### Using a cache to improve performance diff --git a/http/banner.css b/http/banner.css index 4eba234f..61e7d9e6 100644 --- a/http/banner.css +++ b/http/banner.css @@ -23,10 +23,10 @@ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed', GradientType=0 ); } .message-banner-desktop p.title { - font-size: 14px; - margin: 0; - margin-top: 5px; - margin-bottom: 8px; + font-size: 14px; + margin: 0; + margin-top: 5px; + margin-bottom: 8px; } .message-banner-desktop .subtitle { width: 210px; @@ -37,46 +37,46 @@ } .message-banner-mobile { - display: none; - text-align: center; - position: absolute; - background: #fe003e; - color: #fff; - font-family: Arial; - -webkit-transform:rotate(-90deg); - -moz-transform:rotate(-90deg); - -o-transform: rotate(-90deg); - -ms-transform:rotate(-90deg); - transform: rotate(-90deg); - left: -58px; - bottom: 58px; - padding: 5px; + display: none; + text-align: center; + position: absolute; + background: #fe003e; + color: #fff; + font-family: Arial; + -webkit-transform:rotate(-90deg); + -moz-transform:rotate(-90deg); + -o-transform: rotate(-90deg); + -ms-transform:rotate(-90deg); + transform: rotate(-90deg); + left: -58px; + bottom: 58px; + padding: 5px; } .message-banner-mobile .title { - margin: 0; - font-size: 25px; + margin: 0; + font-size: 25px; } .message-banner-mobile .subtitle { - margin: 0; - font-size: 11px; + margin: 0; + font-size: 11px; } .message-banner-mobile:hover { - background: #f37b30; + background: #f37b30; } .message-banner-mobile .message-link, .message-banner-desktop .message-link { - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 99999; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 99999; } header.header { position: relative !important; } header.header h2 { - margin-bottom: 3rem; + margin-bottom: 3rem; } .icon-googleplus:before, .icon-mastodon:before { @@ -84,26 +84,26 @@ header.header h2 { left: 3px; } .select-projects { - background-position: bottom 0.15em right 0.3em; - width: 100%; + background-position: bottom 0.15em right 0.3em; + width: 100%; } @media screen and (max-width: 600px) { - .message-banner-desktop { - display: none; - } - .message-banner-mobile { - display: block; - } + .message-banner-desktop { + display: none; + } + .message-banner-mobile { + display: block; + } } @media screen and (max-width: 400px) { - .message-banner-mobile { - bottom: 50px; - } - .message-banner-mobile .title { - font-size: 20px; - } - .message-banner-mobile .subtitle { - font-size: 9.5px; - } + .message-banner-mobile { + bottom: 50px; + } + .message-banner-mobile .title { + font-size: 20px; + } + .message-banner-mobile .subtitle { + font-size: 9.5px; + } } diff --git a/script.sh b/script.sh index 8fd8bb5e..993453fe 100755 --- a/script.sh +++ b/script.sh @@ -105,13 +105,13 @@ list_blobs() v=`echo $opt2 | version_rev` if [ "$opt1" = '-p' ]; then - # "path" option: return blob hash and full path + # "path" option: return blob hash and full path format='\1 \2' elif [ "$opt1" = '-f' ]; then - # "file" option: return blob hash and file name (without its path) + # "file" option: return blob hash and file name (without its path) format='\1 \4' else - # default option: return only blob hash + # default option: return only blob hash format='\1' v=`echo $opt1 | version_rev` fi diff --git a/templates/header.html b/templates/header.html index 2248f4ad..50305c88 100644 --- a/templates/header.html +++ b/templates/header.html @@ -1,37 +1,37 @@ <header class="header"> - <div class="message-banner-mobile"> - <p class="title">Training sessions</p> - <p class="subtitle">Kernel and Embedded Linux</p> - <a class="message-link" target="_blank" href="https://bootlin.com/training/sessions/"></a> - </div> - <div class="message-banner-desktop"> - <p class="title">Next training sessions</p> - <div class="subtitle">Linux Kernel: March 16-20</div> - <div class="subtitle">Embedded Linux: May 11-15</div> - <div class="subtitle">and on-site sessions</div> - <a class="message-link" target="_blank" href="https://bootlin.com/training/sessions/"></a> - </div> - <h1> - <object data="/images/bootlin-logo-white.svg" type="image/svg+xml"> - <!-- If the browser doesn't support SVG --> - <img src="/images/bootlin-logo-white.png" alt="Bootlin logo" /> - </object> - </h1> - <h2>Elixir Cross Referencer</h2> - <nav> - <ul class="nav-links"> - <li><a href="https://bootlin.com/">Home</a></li> - <li><a href="https://bootlin.com/engineering/">Engineering</a></li> - <li><a href="https://bootlin.com/training/">Training</a></li> - <li><a href="https://bootlin.com/docs/">Docs</a></li> - <li><a href="https://bootlin.com/community/">Community</a></li> - <li><a href="https://bootlin.com/company/">Company</a></li> - </ul> - <ul class="social-icons"> - <li><a target="_blank" class="icon-twitter" href="http://twitter.com/bootlincom">twitter</a></li> - <li><a target="_blank" class="icon-mastodon" href="https://fosstodon.org/@bootlin">mastodon</a></li> - <li><a target="_blank" class="icon-linkedin" href="https://www.linkedin.com/groups/4501089">linkedin</a></li> - <li><a target="_blank" class="icon-github" href="https://github.com/bootlin">github</a></li> - </ul> - </nav> + <div class="message-banner-mobile"> + <p class="title">Training sessions</p> + <p class="subtitle">Kernel and Embedded Linux</p> + <a class="message-link" target="_blank" href="https://bootlin.com/training/sessions/"></a> + </div> + <div class="message-banner-desktop"> + <p class="title">Next training sessions</p> + <div class="subtitle">Linux Kernel: March 16-20</div> + <div class="subtitle">Embedded Linux: May 11-15</div> + <div class="subtitle">and on-site sessions</div> + <a class="message-link" target="_blank" href="https://bootlin.com/training/sessions/"></a> + </div> + <h1> + <object data="/images/bootlin-logo-white.svg" type="image/svg+xml"> + <!-- If the browser doesn't support SVG --> + <img src="/images/bootlin-logo-white.png" alt="Bootlin logo" /> + </object> + </h1> + <h2>Elixir Cross Referencer</h2> + <nav> + <ul class="nav-links"> + <li><a href="https://bootlin.com/">Home</a></li> + <li><a href="https://bootlin.com/engineering/">Engineering</a></li> + <li><a href="https://bootlin.com/training/">Training</a></li> + <li><a href="https://bootlin.com/docs/">Docs</a></li> + <li><a href="https://bootlin.com/community/">Community</a></li> + <li><a href="https://bootlin.com/company/">Company</a></li> + </ul> + <ul class="social-icons"> + <li><a target="_blank" class="icon-twitter" href="http://twitter.com/bootlincom">twitter</a></li> + <li><a target="_blank" class="icon-mastodon" href="https://fosstodon.org/@bootlin">mastodon</a></li> + <li><a target="_blank" class="icon-linkedin" href="https://www.linkedin.com/groups/4501089">linkedin</a></li> + <li><a target="_blank" class="icon-github" href="https://github.com/bootlin">github</a></li> + </ul> + </nav> </header> diff --git a/templates/layout.html b/templates/layout.html index e0941602..63f7b3c4 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,64 +1,64 @@ <!doctype html> <html class="no-js"> - <head> - <meta charset="utf-8"> - <meta http-equiv="x-ua-compatible" content="ie=edge"> - <title>{{title}} - - - + + + + {{title}} + + + - - - - - -
- {% include 'header.html' %} -
- - -
-
-
- {{main}} -
- -
- -
- - + + + + + +
+ {% include 'header.html' %} +
+ + +
+
+
+ {{main}} +
+ +
+ +
+ + diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 9209c638..a526019e 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -19,9 +19,9 @@ index() { if [ "$remote" != "" ] then - cd $LXR_REPO_DIR - git remote add other $remote - git fetch other + cd $LXR_REPO_DIR + git remote add other $remote + git fetch other fi cd $ELIXIR_INSTALL @@ -37,16 +37,16 @@ index() { if [ "$ELIXIR_ROOT" = "" ] then - echo "Error: ELIXIR_ROOT environment variable not set" - echo "It's where Elixir data are stored" - exit 1 + echo "Error: ELIXIR_ROOT environment variable not set" + echo "It's where Elixir data are stored" + exit 1 fi if [ "$ELIXIR_INSTALL" = "" ] then - echo "Error: ELIXIR_INSTALL environment variable not set" - echo "It's where Elixir code is stored" - exit 1 + echo "Error: ELIXIR_INSTALL environment variable not set" + echo "It's where Elixir code is stored" + exit 1 fi index amazon-freertos https://github.com/aws/amazon-freertos.git & From 04e9f85474c5e0a26375d98eb0c005b27350216c Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 21 Mar 2020 09:44:49 -0400 Subject: [PATCH 026/529] Add t/interact.pl for interactive testing Run `./t/interact.pl` and it will build a database from `t/tree` and leave you in a shell so you can experiment. When you exit the shell, the temporary repo and database will be cleaned up. Also: - TestEnvironment: add report() function to produce a human-readable summary of the environment - TestHelpers: turn on `strict` and `warnings` so the caller doesn't have to manually `use strict` and `use warnings`. --- t/TestEnvironment.pm | 17 +++++++++++++++ t/TestHelpers.pm | 14 ++++++++++++- t/interact.pl | 50 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100755 t/interact.pl diff --git a/t/TestEnvironment.pm b/t/TestEnvironment.pm index 3a9d8e7b..cf6d6482 100644 --- a/t/TestEnvironment.pm +++ b/t/TestEnvironment.pm @@ -195,6 +195,23 @@ sub update_env { return $self; } #update_env() +=head2 report + +Returns a human-readable report of the current environment's state. + +=cut + +sub report { + my $self = shift; + return <lxr_repo_dir]} +Database: @{[$self->lxr_data_dir]} +script.sh: @{[$self->script_sh]} +update.py: @{[$self->update_py]} +query.py: @{[$self->query_py]} +EOT +} #report() + =head2 DESTROY Destructor. Called automatically. diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm index b920da19..51cf6976 100644 --- a/t/TestHelpers.pm +++ b/t/TestHelpers.pm @@ -17,6 +17,7 @@ TestHelpers - Common routines for use in tests =head1 SYNOPSIS C, and all the functions below will be exported. +TestHelpers also turns on L and L. =cut @@ -210,10 +211,21 @@ sub run_produces_ok { } #run_produces_ok() +=head2 import + +Set up. Called automatically. + +=cut + +sub import { + __PACKAGE__->export_to_level(1, @_); + strict->import; + warnings->import; +} #import() + 1; __END__ - =head1 AUTHOR Christopher White, C<< >> diff --git a/t/interact.pl b/t/interact.pl new file mode 100755 index 00000000..b8ec1ca8 --- /dev/null +++ b/t/interact.pl @@ -0,0 +1,50 @@ +#!/usr/bin/env perl +# t/interact.pl: Open a shell on a DB of the files in tree/ . +# +# Copyright (c) 2020 Christopher White, . +# +# Elixir is free software; you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Elixir is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# # You should have received a copy of the GNU Affero General Public License +# along with Elixir. If not, see . +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file uses core Perl modules only. + +use autodie; + +use FindBin '$Bin'; +use lib $Bin; + +use TestEnvironment; +use TestHelpers; + +# =========================================================================== +# Main + +# These two lines are all that's required to set up for a test. +my $tenv = TestEnvironment->new; +$tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; + +print($tenv->report); + +# Make some convenient symlinks +chdir($tenv->lxr_repo_dir); +system(qw(ln -s), $tenv->script_sh, 'script.sh') == 0 + or warn "error creating ./script.sh: $? $!"; +system(qw(ln -s), $tenv->update_py, 'update.py') == 0 + or warn "error creating ./update.py: $? $!"; +system(qw(ln -s), $tenv->query_py, 'query.py') == 0 + or warn "error creating ./query.py: $? $!"; + +print("Exit when done, and the repository and database will be removed.\n"); +system($ENV{SHELL} || 'sh'); + From c42da0eefcebf49c5608533cc3c23f8487666299 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Sat, 21 Mar 2020 10:58:18 -0400 Subject: [PATCH 027/529] CR1 --- t/100-basic.t | 4 ---- t/200-api.t | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 t/200-api.t diff --git a/t/100-basic.t b/t/100-basic.t index 86d9a86b..5d837fe2 100644 --- a/t/100-basic.t +++ b/t/100-basic.t @@ -113,10 +113,6 @@ run_produces_ok('file query (existent), .h', [qr{i2c-core\.h}, qr{\bWe\b}], MUST_SUCCEED); -# Test the api using pytest. Prints the test results. - -run_produces_ok('api pytest suite', ["pytest", "-v"], [], MUST_SUCCEED, 1); - #system('bash'); # Uncomment this if you want to interact with the test repo done_testing; diff --git a/t/200-api.t b/t/200-api.t new file mode 100644 index 00000000..b7e85b1c --- /dev/null +++ b/t/200-api.t @@ -0,0 +1,36 @@ +#!/usr/bin/env perl +# 200-api.t: Test elixir's REST api against the files in tree/ . +# +# Copyright (c) 2020 Tamir Carmeli, . +# +# Elixir is free software; you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Elixir is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# # You should have received a copy of the GNU Affero General Public License +# along with Elixir. If not, see . +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file uses core Perl modules only. + +use FindBin '$Bin'; +use lib $Bin; + +use Test::More; + +use TestEnvironment; +use TestHelpers; + +my $tenv = TestEnvironment->new; +$tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; + +# Test the api using pytest. Prints the test results +run_produces_ok('api pytest suite', ["pytest", "-v"], [], MUST_SUCCEED, 1); + +done_testing; \ No newline at end of file From 59b45e86cc90e8ba97aad71b11d49b3783656a87 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 2 Apr 2020 16:10:28 +0200 Subject: [PATCH 028/529] http/web.py: create /tmp/elixir-errors if non existing Signed-off-by: Michael Opdenacker --- http/web.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/http/web.py b/http/web.py index fee22031..d95353a8 100755 --- a/http/web.py +++ b/http/web.py @@ -28,15 +28,21 @@ def print(arg, end='\n'): global outputBuffer outputBuffer.write(arg + end) -# Enable CGI Trackback Manager for debugging (https://docs.python.org/fr/3/library/cgitb.html) import cgitb -cgitb.enable(display=0, logdir='/tmp/elixir-errors', format='text') - import cgi import os import re from re import search, sub +# Create /tmp/elixir-errors if not existing yet (could happen after a reboot) +errdir = '/tmp/elixir-errors' + +if not(os.path.isdir(errdir)) + os.makedirs(errdir, exist_ok=True) + +# Enable CGI Trackback Manager for debugging (https://docs.python.org/fr/3/library/cgitb.html) +cgitb.enable(display=0, logdir=errdir, format='text') + ident = '' status = 200 From ae6fb0dbb24f56c395d849e3b881ec70f2dc6865 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 2 Apr 2020 16:22:43 +0200 Subject: [PATCH 029/529] Fix syntax error Signed-off-by: Michael Opdenacker --- http/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/web.py b/http/web.py index d95353a8..bb959261 100755 --- a/http/web.py +++ b/http/web.py @@ -37,7 +37,7 @@ def print(arg, end='\n'): # Create /tmp/elixir-errors if not existing yet (could happen after a reboot) errdir = '/tmp/elixir-errors' -if not(os.path.isdir(errdir)) +if not(os.path.isdir(errdir)): os.makedirs(errdir, exist_ok=True) # Enable CGI Trackback Manager for debugging (https://docs.python.org/fr/3/library/cgitb.html) From 14dc2507441ae86045cdb6fbd4e8fa7a736c6325 Mon Sep 17 00:00:00 2001 From: Christopher White Date: Sat, 21 Mar 2020 11:59:26 -0400 Subject: [PATCH 030/529] Added initial test of doc-comment extraction Test does not currently pass, in true TDD style. --- t/300-doc-comments.t | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 t/300-doc-comments.t diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t new file mode 100644 index 00000000..99e0bdd0 --- /dev/null +++ b/t/300-doc-comments.t @@ -0,0 +1,63 @@ +#!/usr/bin/env perl +# 300-doc-comments.t: Test elixir doc-comment extraction against the files in tree/ . +# +# Copyright (c) 2020 Christopher White. +# Copyright (c) 2020 D3 Engineering, LLC. +# By Christopher White, . +# +# Elixir is free software; you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Elixir is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Elixir. If not, see . +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file uses core Perl modules only. + +use autodie; # note: still need to check system() calls manually + +use FindBin '$Bin'; +use lib $Bin; + +use Test::More; + +use TestEnvironment; +use TestHelpers; + +# =========================================================================== +# Main + +# Set up +my $tenv = TestEnvironment->new; +$tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; + +ok_or_die( -d $tenv->lxr_data_dir, 'database dir exists', + "Database dir @{[$tenv->lxr_data_dir]} not present"); + +# Spot-check some identifiers + +run_produces_ok('doc-comment query (nonexistent)', + [$tenv->query_py, qw(v5.4 ident SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH)], + [ + qr{^Documented in:}, + { not => qr{/} } # No file paths in the output + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, function, documented in C file)', + [$tenv->query_py, qw(v5.4 ident i2c_acpi_get_i2c_resource)], + [ + qr{^Documented in:}, + qr{drivers/i2c/i2c-core-acpi\.c.+\b45\b}, + ], + MUST_SUCCEED); + +done_testing; From 60f206c9fa4a1076feaceeaf178ed5321a7e9cd0 Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 21 Mar 2020 12:01:51 -0400 Subject: [PATCH 031/529] TestHelpers: report errors at caller's line number When a test fails, report the line number in the .t file that test was invoked from, not the line number in TestHelpers.pm itself. New function `line_mark_string()` adds the filename and line-number information to strings of code, which are then run using `eval`. --- t/TestHelpers.pm | 107 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 9 deletions(-) diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm index b7773044..ea56bc05 100644 --- a/t/TestHelpers.pm +++ b/t/TestHelpers.pm @@ -37,11 +37,21 @@ use Symbol; use Test::More; -# Automatically export all the functions below +# Automatically export all the functions listed in @EXPORT. The functions +# in @EXPORT_OK can be exported on request. use parent 'Exporter'; -our @EXPORT; -BEGIN { @EXPORT = qw(sibling_abs_path find_program run_program ok_or_die - run_produces_ok MUST_SUCCEED); } +our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); +BEGIN { + @EXPORT = qw(sibling_abs_path find_program run_program ok_or_die + run_produces_ok MUST_SUCCEED); + @EXPORT_OK = qw(line_mark_string); + %EXPORT_TAGS = ( + all => [@EXPORT, @EXPORT_OK], + ); +} + +# Forwards for internal functions +sub line_mark_string; # =========================================================================== @@ -131,9 +141,8 @@ Run a test, but die if it fails. Usage: sub ok_or_die { my ($cond, $msg, $err_msg) = @_; - my $line = (caller)[2]; - my $retval = eval <{not} eq 'Regexp') { my $re = $entry->{not}; - ok( !(grep { m{$re} } @outlines), "$desc: output excludes $re" ); + eval line_mark_string 1, + q(ok( !(grep { m{$re} } @outlines), "$desc: output excludes $re" )); } else { die "Invalid entry $entry"; @@ -216,6 +229,82 @@ sub run_produces_ok { } #run_produces_ok() +=head1 INTERNAL FUNCTIONS + +These are ones you probably won't need to call. + +=head2 _croak + +Lazy L + +=cut + +sub _croak { + require Carp; + goto &Carp::croak; +} + +=head2 line_mark_string + +Add a C<#line> directive to a string. Usage: + +To use the caller's filename and line number: + + my $str = line_mark_string < will be used for the +filename and line number. + +In the first and third forms, the C<#line> directive will point to the line +after the C invocation, i.e., the first line of . +Generally, C<$contents> will be source code, although this is not required. + +C<$contents> must be defined, but can be empty. + +=cut + +sub line_mark_string { + my ($contents, $filename, $line); + + if(@_ == 1) { + $contents = $_[0]; + (undef, $filename, $line) = caller; + ++$line; + } elsif(@_ == 2) { + (undef, $filename, $line) = caller($_[0]); + $contents = $_[1]; + } elsif(@_ == 3) { + ($filename, $line, $contents) = @_; + ++$line; + } else { + _croak "Invalid invocation"; + } + + _croak "Need text" unless defined $contents; + die "Couldn't get location information" unless $filename && $line; + + $filename =~ s/"/-/g; + + return < Date: Thu, 2 Apr 2020 13:03:03 -0400 Subject: [PATCH 032/529] Add doc-comment extraction - find-file-doc-comments.pl: new file - data.py: Add database to store doc-comment locations - script.sh: Add parse-docs subcommand - update.py: - Add code to process doc comments - Update some variable names in hopes of reducing confusion - query.py: - Add code to report doc comments - Update some variable names in hopes of reducing confusion Also: - t/TestEnvironment.pm: Add find_doc attribute - t/interact.pl: Don't die if update.py fails - t/TestHelpers.pm: Permit checking specific sections of query.py output - t/300: update regexes per the preceding - gitignore tags (ctags output) and .cache (api_test.py output) --- .gitignore | 5 ++ data.py | 11 +++ find-file-doc-comments.pl | 100 +++++++++++++++++++++++++++ query.py | 68 +++++++++++++------ script.sh | 25 ++++++- t/300-doc-comments.t | 12 +++- t/TestEnvironment.pm | 18 +++-- t/TestHelpers.pm | 139 +++++++++++++++++++++++++++++++++----- t/interact.pl | 13 ++-- update.py | 75 ++++++++++++++------ 10 files changed, 396 insertions(+), 70 deletions(-) create mode 100755 find-file-doc-comments.pl diff --git a/.gitignore b/.gitignore index 5ac14179..5cc790bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ +# Generated files __pycache__ +tags +/.cache/ + +# Web-specific http/images http/*.html http/favicon.ico diff --git a/data.py b/data.py index e09ba5ca..8f088bd3 100755 --- a/data.py +++ b/data.py @@ -49,6 +49,8 @@ maxId = 999999999 class DefList: + '''Stores associations between a blob ID, a type (e.g., "function"), + and a line number.''' def __init__(self, data=b''): self.data = data @@ -75,6 +77,8 @@ def pack(self): return self.data class PathList: + '''Stores associations between a blob ID and a file path. + Inserted by update.py sorted by blob ID.''' def __init__(self, data=b''): self.data = data @@ -96,6 +100,7 @@ def pack(self): return self.data class RefList: + '''Stores a mapping from blob ID to list of lines.''' def __init__(self, data=b''): self.data = data @@ -162,9 +167,15 @@ def __init__(self, dir, readonly=True): ro = readonly self.vars = BsdDB(dir + '/variables.db', ro, lambda x: int(x.decode()) ) + # Key-value store of basic information self.blob = BsdDB(dir + '/blobs.db', ro, lambda x: int(x.decode()) ) + # Map hash to sequential integer serial number self.hash = BsdDB(dir + '/hashes.db', ro, lambda x: x ) + # Map serial number back to hash self.file = BsdDB(dir + '/filenames.db', ro, lambda x: x.decode() ) + # Map serial number to filename self.vers = BsdDB(dir + '/versions.db', ro, PathList) self.defs = BsdDB(dir + '/definitions.db', ro, DefList) self.refs = BsdDB(dir + '/references.db', ro, RefList) + self.docs = BsdDB(dir + '/doccomments.db', ro, RefList) + # Use a RefList in case there are multiple doc comments for an identifier diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl new file mode 100755 index 00000000..56a90b4b --- /dev/null +++ b/find-file-doc-comments.pl @@ -0,0 +1,100 @@ +#!/usr/bin/env perl +# find-file-doc-comments.pl: Find the doc comments for a file. +# Usage: find-file-doc-comments.pl +# By Christopher White +# Copyright (c) 2019 D3 Engineering, LLC. +# Licensed AGPLv3 + +use 5.010001; +use strict; +use warnings; +use autodie; + +my $VERBOSE = $ENV{V}; + +exit main(@ARGV); + +sub main { + die "Need a filename" unless @_; + + # Do `script.sh parse-defs` on the file + my @ctags = qx{ ctags -x --c-kinds=+p-m --language-force=C "$_[0]" | + grep -av "^operator " | + awk '{print \$1" "\$2" "\$3}' }; + die "Could not get ctags: $!" if $!; + print "No ctags results" if $VERBOSE && !@ctags; + return 0 unless @ctags; + + # Make a list of [name, type, line] arrays + my @ctags_parsed = map { [split] } @ctags; + + # Flip it around to index functions by line + my %function_lines; + for my $tag (@ctags_parsed) { + next unless $tag->[1] eq 'function'; + $function_lines{$tag->[2]} = $tag->[0]; + } + + if($VERBOSE) { + for my $tag (@ctags_parsed) { + say $tag->[2], ': ', $tag->[0], ' is a(n) ', $tag->[1]; + } + } + + # Read the source file + open my $fh, '<', $_[0]; + my @source_lines = (undef, <$fh>); + # undef => indices in @source_lines match ctags's 1-based linenos + close $fh; + + # Work backwards through the file and look for doc comments + my %doc_comments; + + my $doc_comment_opener = qr{^\s*\/\*\*(?:\s|$)}; # Start of doc comment + + for(my $lineno = $#source_lines ; $lineno >= 1 ; --$lineno) { + next unless exists $function_lines{$lineno}; + my $func_name = $function_lines{$lineno}; + print "Checking for $func_name @ $lineno\n" if $VERBOSE; + + my $this_doc_comment_header = + qr{^\s+\*\s+\Q$func_name\E(?:\s|\(|$)}; + print " Regex is -$this_doc_comment_header-\n" if $VERBOSE; + --$lineno; + print " Line $lineno is: $source_lines[$lineno]\n" if $VERBOSE; + + # Find the last line that could be a doc-comment header + # for this function. + while($source_lines[$lineno] =~ + qr{ + ^\s*$ # Empty line + | ^\s+\*\/ # End of comment + | ^\s+\*(?:\s|$) # Continuation of comment + | $this_doc_comment_header + }x) { + print "$source_lines[$lineno] passed\n" if $VERBOSE; + --$lineno; + } + ++$lineno; # Check the last line that matched, + # because we may have just skipped past $this_doc_comment_header + + # Is it actually a header for this function? + print "Checking $source_lines[$lineno] for header\n" if $VERBOSE; + next unless $source_lines[$lineno] =~ $this_doc_comment_header; + + # We have found a header. Confirm it's a doc comment. + --$lineno; + next unless $source_lines[$lineno] =~ $doc_comment_opener; + print " * Match\n" if $VERBOSE; + + # We have found a doc comment for this function! Record it. + push @{$doc_comments{$func_name}}, $lineno; + } + + # Report the doc comments for each function + while(my ($funcname, $comment_lines) = each %doc_comments) { + print "$funcname $_\n" foreach @$comment_lines; + } + + return 0; +} diff --git a/query.py b/query.py index 4fc07de0..1825e370 100755 --- a/query.py +++ b/query.py @@ -146,37 +146,60 @@ def query(cmd, *args): symbol_definitions = [] symbol_references = [] + symbol_doccomments = [] if not db.defs.exists(ident): - return symbol_definitions, symbol_references + return symbol_definitions, symbol_references, symbol_doccomments if not db.vers.exists(version): - return symbol_definitions, symbol_references + return symbol_definitions, symbol_references, symbol_doccomments - vers = db.vers.get(version).iter() - defs = db.defs.get(ident).iter(dummy=True) - # FIXME: see why we can have a discrepancy between defs and refs + files_this_version = db.vers.get(version).iter() + defs_this_ident = db.defs.get(ident).iter(dummy=True) + # FIXME: see why we can have a discrepancy between defs_this_ident and refs if db.refs.exists(ident): refs = db.refs.get(ident).iter(dummy=True) else: refs = data.RefList().iter(dummy=True) - id2, type, dline = next(defs) - id3, rlines = next(refs) + if db.docs.exists(ident): + docs = db.docs.get(ident).iter(dummy=True) + else: + docs = data.RefList().iter(dummy=True) + + # vers, defs, refs, and docs are all populated by update.py in order of + # idx, and there is a one-to-one mapping between blob hashes and idx + # values. Therefore, we can sequentially step through the defs, refs, + # and docs for each file in a version. + + def_idx, def_type, def_line = next(defs_this_ident) + ref_idx, ref_lines = next(refs) + doc_idx, doc_line = next(docs) dBuf = [] rBuf = [] + docBuf = [] + + for file_idx, file_path in files_this_version: + # Advance defs, refs, and docs to the current file + while def_idx < file_idx: + def_idx, def_type, def_line = next(defs_this_ident) + while ref_idx < file_idx: + ref_idx, ref_lines = next(refs) + while doc_idx < file_idx: + doc_idx, doc_line = next(docs) + + # Copy information about this identifier into dBuf, rBuf, and docBuf. + while def_idx == file_idx: + dBuf.append((file_path, def_type, def_line)) + def_idx, def_type, def_line = next(defs_this_ident) + + if ref_idx == file_idx: + rBuf.append((file_path, ref_lines)) + + if doc_idx == file_idx: # TODO should this be a `while`? + docBuf.append((file_path, doc_line)) - for id1, path in vers: - while id1 > id2: - id2, type, dline = next(defs) - while id1 > id3: - id3, rlines = next(refs) - while id1 == id2: - dBuf.append((path, type, dline)) - id2, type, dline = next(defs) - if id1 == id3: - rBuf.append((path, rlines)) for path, type, dline in sorted(dBuf): symbol_definitions.append(SymbolInstance(path, dline, type)) @@ -184,13 +207,16 @@ def query(cmd, *args): for path, rlines in sorted(rBuf): symbol_references.append(SymbolInstance(path, rlines)) - return symbol_definitions, symbol_references + for path, docline in sorted(docBuf): + symbol_doccomments.append(SymbolInstance(path, docline)) + + return symbol_definitions, symbol_references, symbol_doccomments else: return('Unknown subcommand: ' + cmd + '\n') def cmd_ident(version, ident, **kwargs): - symbol_definitions, symbol_references = query("ident", version, ident) + symbol_definitions, symbol_references, symbol_doccomments = query("ident", version, ident) print("Symbol Definitions:") for symbol_definition in symbol_definitions: print(symbol_definition) @@ -199,6 +225,10 @@ def cmd_ident(version, ident, **kwargs): for symbol_reference in symbol_references: print(symbol_reference) + print("\nDocumented in:") + for symbol_doccomment in symbol_doccomments: + print(symbol_doccomment) + def cmd_file(version, path, **kwargs): code = query("file", version, path) print(code) diff --git a/script.sh b/script.sh index 993453fe..e2f62a87 100755 --- a/script.sh +++ b/script.sh @@ -2,8 +2,8 @@ # This file is part of Elixir, a source code cross-referencer. # -# Copyright (C) 2017 Mikaël Bouillot -# +# Copyright (C) 2017--2020 Mikaël Bouillot +# and contributors # # Elixir is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -23,6 +23,13 @@ if [ ! -d "$LXR_REPO_DIR" ]; then exit 1 fi +# Get our path so we can find peer find-file-doc-comments.pl later +cur_dir=`pwd` +script_path=`realpath "$0"` +cd `dirname "$script_path"` +script_dir=`pwd` +cd "$cur_dir" + version_dir() { cat; @@ -140,6 +147,16 @@ parse_defs() rmdir $tmp } +parse_docs() +{ + tmpfile=`mktemp` + + git cat-file blob "$opt1" > "$tmpfile" + "$script_dir/find-file-doc-comments.pl" "$tmpfile" + + rm -rf "$tmpfile" +} + project=$(basename `dirname $LXR_REPO_DIR`) plugin=projects/$project.sh @@ -208,6 +225,10 @@ case $cmd in parse_defs ;; + parse-docs) + parse_docs + ;; + help) echo "Usage: $0 subcommand [args]..." exit 1 diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index 99e0bdd0..ddc77e58 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -48,7 +48,15 @@ run_produces_ok('doc-comment query (nonexistent)', [$tenv->query_py, qw(v5.4 ident SOME_NONEXISTENT_IDENTIFIER_XYZZY_PLUGH)], [ qr{^Documented in:}, - { not => qr{/} } # No file paths in the output + {doc => { not => qr{/} }}, # No file paths in the doc section + ], + MUST_SUCCEED); + +run_produces_ok('doc-comment query (existent but not documented)', + [$tenv->query_py, qw(v5.4 ident gsb_buffer)], # in drivers/i2c/i2c-core-acpi.c + [ + qr{^Documented in:}, + {doc => { not => qr{/} }} ], MUST_SUCCEED); @@ -56,7 +64,7 @@ run_produces_ok('ident query (existent, function, documented in C file)', [$tenv->query_py, qw(v5.4 ident i2c_acpi_get_i2c_resource)], [ qr{^Documented in:}, - qr{drivers/i2c/i2c-core-acpi\.c.+\b45\b}, + {doc => qr{drivers/i2c/i2c-core-acpi\.c.+\b45\b}}, ], MUST_SUCCEED); diff --git a/t/TestEnvironment.pm b/t/TestEnvironment.pm index cf6d6482..e1ed8b7d 100644 --- a/t/TestEnvironment.pm +++ b/t/TestEnvironment.pm @@ -69,6 +69,10 @@ As L, but for C. As L, but for C. +=head2 find_doc + +As L, but for C. + =cut has lxr_data_dir => (); @@ -82,6 +86,9 @@ has query_py => ( has update_py => ( default => sub { find_program('update.py') } ); +has find_doc => ( + default => sub { find_program('find-file-doc-comments.pl') } +); # Internal attributes @@ -204,11 +211,12 @@ Returns a human-readable report of the current environment's state. sub report { my $self = shift; return <lxr_repo_dir]} -Database: @{[$self->lxr_data_dir]} -script.sh: @{[$self->script_sh]} -update.py: @{[$self->update_py]} -query.py: @{[$self->query_py]} +Repository: @{[$self->lxr_repo_dir || '']} +Database: @{[$self->lxr_data_dir || '']} +script.sh: @{[$self->script_sh || '']} +update.py: @{[$self->update_py || '']} +query.py: @{[$self->query_py || '']} +find-file-doc-comments.pl: @{[$self->find_doc || '']} EOT } #report() diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm index ea56bc05..f6fa7464 100644 --- a/t/TestHelpers.pm +++ b/t/TestHelpers.pm @@ -48,7 +48,7 @@ BEGIN { %EXPORT_TAGS = ( all => [@EXPORT, @EXPORT_OK], ); -} +} #BEGIN # Forwards for internal functions sub line_mark_string; @@ -80,7 +80,7 @@ this file. Usage: sub sibling_abs_path { return File::Spec->rel2abs(File::Spec->catfile($FindBin::Bin, @_)); -} +} #sibling_abs_path() =head2 find_program @@ -157,12 +157,47 @@ Usage: run_produces_ok($desc, \@program_and_args, \@expected_regexes, $mustSucceed, $printOutput) -The test passes if each regex in C<@expected_regexes> matches at least one -line in the output of C<@program_and_args>, and if each C<< { not => regex } >> -in C<@expected_regexes> is NOT found in that output. +The test passes if each condition in C<@conditions> is true. If C<$mustSucceed> is true, also tests for exit status 0 and empty stderr. If C<$printOutput> is true, prints the output of C<@program_and_args>. +=head3 Conditions that can be used any time + +=over + +=item * + +A regex: true if the regex matches at least one line in the output of +C<@program_and_args> + +=item * + +C<< { not => regex } >>: true if the regex is NOT found in any line of +the output of C<@program_and_args>. + +=back + +=head3 Conditions for the output of C + +=over + +=item * + +C<< { def => regex } >>: true if the regex matches at least one line in +the "Symbol Definitions" section of the output of C<@program_and_args>. + +=item * + +C<< { ref => regex } >>: true if the regex matches at least one line in +the "Symbol References" section of the output of C<@program_and_args>. + +=item * + +C<< { doc => regex } >>: true if the regex matches at least one line in +the "Documented in" section of the output of C<@program_and_args>. + +=back + =cut sub run_produces_ok { @@ -211,20 +246,29 @@ EOT } # Check regexes + my %query_py_output; # filled in only if we see a def/ref/doc for my $entry (@$lrRegexes) { - - if(ref $entry eq 'Regexp') { - eval line_mark_string 1, - q(ok( (grep { m{$entry} } @outlines), "$desc: output includes $entry" )); - - } elsif(ref $entry eq 'HASH' && ref $entry->{not} eq 'Regexp') { - my $re = $entry->{not}; - eval line_mark_string 1, - q(ok( !(grep { m{$re} } @outlines), "$desc: output excludes $re" )); - + my ($re, $negated, $source) = _parse_condition($entry); + + # Parse query.py output if we need it and haven't done so + %query_py_output = _parseq(@outlines) + if $source ne 'output' && !%query_py_output; + + # Build a line of test code to run + my $test = 'ok( '; + $test .= '!' if $negated; + $test .= '(grep { m{$re} } '; + if($source eq 'output') { + $test .= '@outlines'; } else { - die "Invalid entry $entry"; + $test .= '@{$query_py_output{' . $source . '}}'; } + $test .= '), "$desc: ' . $source; + $test .= ($negated ? ' excludes ' : ' includes ') . "\Q$re\E" . '");'; + + # Run it + #diag "Running $test"; + eval line_mark_string 1, $test; } #foreach $entry } #run_produces_ok() @@ -233,16 +277,75 @@ EOT These are ones you probably won't need to call. +=head2 _parseq + +Parse the output of query.py. Usage: + + %parsed = _parseq(@lines_of_output); + +=cut + +sub _parseq { + my %retval = { def => [], ref => [], doc => [] }; + my $list; + foreach(@_) { + chomp; + if($_ eq 'Symbol Definitions:') { + $list = 'def'; + next; + } elsif($_ eq 'Symbol References:') { + $list = 'ref'; + next; + } elsif($_ eq 'Documented in:') { + $list = 'doc'; + next; + } + + #diag "Adding `$_' to list $list"; + push @{$retval{$list}}, $_; + } + return %retval; +} #_parseq() + +=head2 _parse_condition + +Parse a condition for L. Usage: + + ($regex, $negated, $source) = _parse_condition($entry[, $source]); + +=cut + +sub _parse_condition { + my ($entry, $source_in) = @_; + my ($regex, $negated, $source); # Return values + + # Basic cases + if(ref $entry eq 'Regexp') { + return ($entry, 0, $source_in || 'output'); + } elsif(ref $entry eq 'HASH' && ref $entry->{not} eq 'Regexp') { + return ($entry->{not}, 1, $source_in || 'output'); + } + + # Sub-keys: chain + if(ref $entry eq 'HASH' && scalar keys %{$entry} == 1) { + return _parse_condition((values %{$entry})[0], (keys %{$entry})[0]); + } + + # If we get here, we don't know how to handle it + die "Invalid entry $entry"; +} #_parse_condition() + + =head2 _croak -Lazy L +Lazy invoker for L. =cut sub _croak { require Carp; goto &Carp::croak; -} +} #_croak() =head2 line_mark_string diff --git a/t/interact.pl b/t/interact.pl index b8ec1ca8..be866119 100755 --- a/t/interact.pl +++ b/t/interact.pl @@ -19,8 +19,6 @@ # # This file uses core Perl modules only. -use autodie; - use FindBin '$Bin'; use lib $Bin; @@ -32,7 +30,10 @@ # These two lines are all that's required to set up for a test. my $tenv = TestEnvironment->new; -$tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; +$tenv->build_repo(sibling_abs_path('tree')); # dies on error +eval { $tenv->build_db; }; +warn "Could not update database: $@" if $@; +$tenv->update_env; print($tenv->report); @@ -44,7 +45,9 @@ or warn "error creating ./update.py: $? $!"; system(qw(ln -s), $tenv->query_py, 'query.py') == 0 or warn "error creating ./query.py: $? $!"; +system(qw(ln -s), $tenv->find_doc, 'find-file-doc-comments.pl') == 0 + or warn "error creating ./find-file-doc-comments.pl: $? $!"; print("Exit when done, and the repository and database will be removed.\n"); -system($ENV{SHELL} || 'sh'); - +my $retval = system($ENV{SHELL} || 'sh'); +exit $retval>>8; diff --git a/update.py b/update.py index a96b581d..6cb4e57e 100755 --- a/update.py +++ b/update.py @@ -18,6 +18,9 @@ # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . +# Throughout, an "idx" is the sequential number associated with a blob. +# This is different from that blob's Git hash. + from sys import argv from lib import scriptLines import lib @@ -25,6 +28,8 @@ import os from data import PathList +verbose = False + db = data.DB(lib.getDataDir(), readonly=False) # Store new blobs hashed and file names (without path) for new tag @@ -39,17 +44,19 @@ def updateBlobIDs(tag): # Get blob hashes and associated file names (without path) blobs = scriptLines('list-blobs', '-f', tag) - newBlobs = [] + newIdxes = [] for blob in blobs: hash, filename = blob.split(b' ',maxsplit=1) if not db.blob.exists(hash): db.blob.put(hash, idx) db.hash.put(idx, hash) db.file.put(idx, filename) - newBlobs.append(idx) + newIdxes.append(idx) + if verbose: + print(f"New blob #{idx} {hash}:{filename}") idx += 1 db.vars.put('numBlobs', idx) - return newBlobs + return newIdxes def updateVersions(tag): @@ -66,13 +73,15 @@ def updateVersions(tag): obj = PathList() for idx, path in buf: obj.append(idx, path) + if verbose: + print(f"Tag {tag}: adding #{idx} {path}") db.vers.put(tag, obj, sync=True) -def updateDefinitions(blobs): - for blob in blobs: - if (blob % 1000 == 0): progress('defs: ' + str(blob)) - hash = db.hash.get(blob) - filename = db.file.get(blob) +def updateDefinitions(idxes): + for idx in idxes: + if (idx % 1000 == 0): progress('defs: ' + str(idx)) + hash = db.hash.get(idx) + filename = db.file.get(idx) if not lib.hasSupportedExt(filename): continue @@ -87,14 +96,16 @@ def updateDefinitions(blobs): else: obj = data.DefList() - obj.append(blob, type, line) + obj.append(idx, type, line) + if verbose: + print(f"def {type} {ident} in #{idx} @ {line}"); db.defs.put(ident, obj) -def updateReferences(blobs): - for blob in blobs: - if (blob % 1000 == 0): progress('refs: ' + str(blob)) - hash = db.hash.get(blob) - filename = db.file.get(blob) +def updateReferences(idxes): + for idx in idxes: + if (idx % 1000 == 0): progress('refs: ' + str(idx)) + hash = db.hash.get(idx) + filename = db.file.get(idx) if not lib.hasSupportedExt(filename): continue @@ -119,9 +130,34 @@ def updateReferences(blobs): else: obj = data.RefList() - obj.append(blob, lines) + obj.append(idx, lines) + if verbose: + print(f"ref: {ident} in #{idx} @ {lines}"); db.refs.put(ident, obj) +def updateDocComments(idxes): + for idx in idxes: + if (idx % 1000 == 0): progress('docs: ' + str(idx)) + hash = db.hash.get(idx) + filename = db.file.get(idx) + + if not lib.hasSupportedExt(filename): continue + + lines = scriptLines('parse-docs', hash, filename) + for l in lines: + ident, line = l.split(b' ') + line = int(line.decode()) + + if db.docs.exists(ident): + obj = db.docs.get(ident) + else: + obj = data.RefList() + + obj.append(idx, str(line)) + if verbose: + print(f"doc: {ident} in #{idx} @ {line}"); + db.docs.put(ident, obj) + def progress(msg): print('{} - {} ({:.0%})'.format(project, msg, tagCount/numTags)) @@ -140,8 +176,9 @@ def progress(msg): for tag in tagBuf: tagCount +=1 - newBlobs = updateBlobIDs(tag) - progress(tag.decode() + ': ' + str(len(newBlobs)) + ' new blobs') + newIdxes = updateBlobIDs(tag) + progress(tag.decode() + ': ' + str(len(newIdxes)) + ' new blobs') updateVersions(tag) - updateDefinitions(newBlobs) - updateReferences(newBlobs) + updateDefinitions(newIdxes) + updateReferences(newIdxes) + updateDocComments(newIdxes) From ca36e56d2ade42c981beec08642d71b8c27d2e6a Mon Sep 17 00:00:00 2001 From: Christopher White Date: Thu, 2 Apr 2020 13:11:16 -0400 Subject: [PATCH 033/529] Front-end: accept (but don't use) doc comments from query() Now that query() returns three elements instead of two, accept all three --- api/api.py | 4 ++-- http/web.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/api.py b/api/api.py index 1fc05349..9ccc5973 100755 --- a/api/api.py +++ b/api/api.py @@ -23,11 +23,11 @@ def on_get(self, req, resp, project, ident): version = req.params['version'] else: raise falcon.HTTPMissingParam('version') - + if version == 'latest': version = query('latest') - symbol_definitions, symbol_references = query('ident', version, ident) + symbol_definitions, symbol_references, symbol_doccomments_UNUSED = query('ident', version, ident) resp.body = json.dumps( { 'definitions': [sym.__dict__ for sym in symbol_definitions], diff --git a/http/web.py b/http/web.py index bb959261..55e38c96 100755 --- a/http/web.py +++ b/http/web.py @@ -294,7 +294,7 @@ def print(arg, end='\n'): elif mode == 'ident': data['title'] = ident+' identifier - '+title_suffix - symbol_definitions, symbol_references = query('ident', tag, ident) + symbol_definitions, symbol_references, symbol_doccomments_UNUSED = query('ident', tag, ident) print('
') if len(symbol_definitions): From a171485c4f4128995fa0a0b40cae706dd0458fc7 Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 4 Apr 2020 12:21:45 -0400 Subject: [PATCH 034/529] doc-comments: support uncommented first function If the first function in a file does not have a doc comment, do not run off the beginning of the file. Fixes #102. Also: - Add t/tree/issue102.c that exhibits this bug. - in find-file-doc-comments.pl and script.sh, exit with a nonzero code if this should recur. --- find-file-doc-comments.pl | 10 ++++++---- script.sh | 2 +- t/300-doc-comments.t | 8 ++++++++ t/TestHelpers.pm | 2 +- t/tree/issue102.c | 14 ++++++++++++++ 5 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 t/tree/issue102.c diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl index 56a90b4b..2391008b 100755 --- a/find-file-doc-comments.pl +++ b/find-file-doc-comments.pl @@ -7,7 +7,7 @@ use 5.010001; use strict; -use warnings; +use warnings FATAL => qw(uninitialized); use autodie; my $VERBOSE = $ENV{V}; @@ -16,6 +16,7 @@ sub main { die "Need a filename" unless @_; + say "Processing file $_[0]" if $VERBOSE; # Do `script.sh parse-defs` on the file my @ctags = qx{ ctags -x --c-kinds=+p-m --language-force=C "$_[0]" | @@ -65,7 +66,7 @@ sub main { # Find the last line that could be a doc-comment header # for this function. - while($source_lines[$lineno] =~ + while($lineno && $source_lines[$lineno] =~ qr{ ^\s*$ # Empty line | ^\s+\*\/ # End of comment @@ -84,7 +85,7 @@ sub main { # We have found a header. Confirm it's a doc comment. --$lineno; - next unless $source_lines[$lineno] =~ $doc_comment_opener; + next unless $lineno > 0 && $source_lines[$lineno] =~ $doc_comment_opener; print " * Match\n" if $VERBOSE; # We have found a doc comment for this function! Record it. @@ -92,7 +93,8 @@ sub main { } # Report the doc comments for each function - while(my ($funcname, $comment_lines) = each %doc_comments) { + for my $funcname (keys %doc_comments) { + my $comment_lines = $doc_comments{$funcname}; print "$funcname $_\n" foreach @$comment_lines; } diff --git a/script.sh b/script.sh index e2f62a87..16002687 100755 --- a/script.sh +++ b/script.sh @@ -152,7 +152,7 @@ parse_docs() tmpfile=`mktemp` git cat-file blob "$opt1" > "$tmpfile" - "$script_dir/find-file-doc-comments.pl" "$tmpfile" + "$script_dir/find-file-doc-comments.pl" "$tmpfile" || exit "$?" rm -rf "$tmpfile" } diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index ddc77e58..7cc4ac30 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -68,4 +68,12 @@ run_produces_ok('ident query (existent, function, documented in C file)', ], MUST_SUCCEED); +run_produces_ok('ident query (existent, function, documented in C file, #102)', + [$tenv->query_py, qw(v5.4 ident documented_function_XYZZY)], + [ + qr{^Documented in:}, + {doc => qr{issue102\.c.+\b6\b}}, + ], + MUST_SUCCEED); + done_testing; diff --git a/t/TestHelpers.pm b/t/TestHelpers.pm index f6fa7464..ef376e27 100644 --- a/t/TestHelpers.pm +++ b/t/TestHelpers.pm @@ -286,7 +286,7 @@ Parse the output of query.py. Usage: =cut sub _parseq { - my %retval = { def => [], ref => [], doc => [] }; + my %retval = ( def => [], ref => [], doc => [] ); my $list; foreach(@_) { chomp; diff --git a/t/tree/issue102.c b/t/tree/issue102.c new file mode 100644 index 00000000..b5cf41c7 --- /dev/null +++ b/t/tree/issue102.c @@ -0,0 +1,14 @@ +void foo(void) +{ +} +/* This file triggers the bug described in #102. */ + +/** + * documented_function_XYZZY + */ +void documented_function_XYZZY(void) +{ +} + +/* t/tree/issue102.c */ +/* SPDX-License-Identifier: CC0-1.0 */ From 893c4aabdb6eca34bc77cc4a5ad3247ddb25d80a Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 6 Apr 2020 17:21:30 +0200 Subject: [PATCH 035/529] update.py: fix syntax issues (typical "C-isms") Signed-off-by: Michael Opdenacker --- update.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/update.py b/update.py index 6cb4e57e..c65d2c5a 100755 --- a/update.py +++ b/update.py @@ -98,7 +98,7 @@ def updateDefinitions(idxes): obj.append(idx, type, line) if verbose: - print(f"def {type} {ident} in #{idx} @ {line}"); + print(f"def {type} {ident} in #{idx} @ {line}") db.defs.put(ident, obj) def updateReferences(idxes): @@ -132,7 +132,7 @@ def updateReferences(idxes): obj.append(idx, lines) if verbose: - print(f"ref: {ident} in #{idx} @ {lines}"); + print(f"ref: {ident} in #{idx} @ {lines}") db.refs.put(ident, obj) def updateDocComments(idxes): @@ -155,7 +155,7 @@ def updateDocComments(idxes): obj.append(idx, str(line)) if verbose: - print(f"doc: {ident} in #{idx} @ {line}"); + print(f"doc: {ident} in #{idx} @ {line}") db.docs.put(ident, obj) def progress(msg): From 3ce8bcf316898e881fe8433b0c623a89611bd92d Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 6 Apr 2020 17:52:04 +0200 Subject: [PATCH 036/529] README.md: now need Python >= 3.6 - For new statements like these (in "update.py"): print(f"New blob #{idx} {hash}:{filename}") Signed-off-by: Michael Opdenacker --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0159b9c3..68a6be6b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Note: this documentation applies to version 1.0 of Elixir. # Requirements -* Python >= 3.5 +* Python >= 3.6 * The Jinja2 and Pygments (>= 2.2) Python libraries * Berkeley DB (and its Python binding) * Exuberant Ctags From 7b21214eb9680a850bf3e636b031c07e5df80de9 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 9 Apr 2020 15:50:07 +0200 Subject: [PATCH 037/529] Add sample project definition for testing Signed-off-by: Michael Opdenacker --- samples/projects/linuxtest.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 samples/projects/linuxtest.sh diff --git a/samples/projects/linuxtest.sh b/samples/projects/linuxtest.sh new file mode 100644 index 00000000..bc875baa --- /dev/null +++ b/samples/projects/linuxtest.sh @@ -0,0 +1,17 @@ +# Elixir definitions for Linux testing (only one tag) +# Copy this file to the "projects" directory + +list_tags() +{ + echo "v5.6.1" +} + +list_tags_h() +{ + echo "v5 v5.6 v5.6.1" +} + +get_latest() +{ + echo "v5.6.1" +} From e7e354221679f4d132a41286448a3daf09f452ac Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Thu, 9 Apr 2020 05:35:42 -0400 Subject: [PATCH 038/529] 1. Testing results for existing identifiers. 2. Testing version parameter options --- t/api_test.py | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/t/api_test.py b/t/api_test.py index a7fafb8e..52e5d6d0 100644 --- a/t/api_test.py +++ b/t/api_test.py @@ -2,6 +2,7 @@ import os import sys +import falcon from falcon import testing api_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..','api')) @@ -15,9 +16,43 @@ def setUp(self): self.app = create_ident_getter() -class TestMyApp(APITest): def test_identifier_not_found(self): result = self.simulate_get('/ident/tree/SOME_NONEXISTENT_IDENTIFIER', query_string="version=latest") self.assertEqual(result.status_code, 200) - self.assertEqual(result.json, {'definitions': [], 'references':[]}) \ No newline at end of file + self.assertEqual(result.json, {'definitions': [], 'references':[]}) + + def test_missing_version(self): + # A get request without a version query string + result = self.simulate_get('/ident/tree/of_i2c_get_board_info') + + self.assertEqual(result.status_code, 400) + + required_response = falcon.HTTPMissingParam('version') + self.assertEqual(result.json["title"], required_response.title) + self.assertEqual(result.json["description"], required_response.description) + + def test_existing_identifier(self): + result_for_specific_version = self.simulate_get('/ident/tree/of_i2c_get_board_info', query_string="version=v5.4") + result_for_latest_version = self.simulate_get('/ident/tree/of_i2c_get_board_info', query_string="version=latest") + + expected_json = { + 'definitions': + [ + {'path': 'drivers/i2c/i2c-core-of.c', 'line': 22, 'type': 'function'}, + {'path': 'drivers/i2c/i2c-core-of.c', 'line': 62, 'type': 'variable'}, + {'path': 'include/linux/i2c.h', 'line': 968, 'type': 'function'}, + {'path': 'include/linux/i2c.h', 'line': 941, 'type': 'prototype'} + ], + 'references': + [ + {'path': 'drivers/i2c/i2c-core-of.c', 'line': '22,62,73', 'type': None}, + {'path': 'include/linux/i2c.h', 'line': '941,968', 'type': None} + ] + } + + self.assertEqual(result_for_specific_version.status_code, 200) + self.assertEqual(result_for_latest_version.status_code, 200) + + self.assertEqual(result_for_specific_version.json, expected_json) + self.assertEqual(result_for_latest_version.json, expected_json) \ No newline at end of file From 3d3fa10084aeac746ac76981dfb4af4aa9111856 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 13 Apr 2020 18:52:53 +0200 Subject: [PATCH 039/529] Ubuntu update: use pytest-3 - pytest seems to be for Python 2 in Ubuntu 18.04 If we use pytest instead of python3-test, it won't find the falcon module provided by python3-falcon - Please raise your hand if this breaks other distros that wouldn't have the "pytest-3" executable. - Updated documentation Signed-off-by: Michael Opdenacker --- README.md | 2 +- t/200-api.t | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68a6be6b..0a443236 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ yum install python36-jinja2 python36-pygments python36-bsddb3 python3-falcon glo > For Debian ``` -sudo apt install python3 python3-jinja2 python3-pygments python3-bsddb3 python3-falcon exuberant-ctags perl git apache2 libapache2-mod-wsgi-py3 +sudo apt install python3 python3-jinja2 python3-pygments python3-bsddb3 python3-falcon python3-pytest exuberant-ctags perl git apache2 libapache2-mod-wsgi-py3 ``` To enable the REST api, follow the installation instructions on [`mod_wsgi`](https://github.com/GrahamDumpleton/mod_wsgi) diff --git a/t/200-api.t b/t/200-api.t index b7e85b1c..96d5ad91 100644 --- a/t/200-api.t +++ b/t/200-api.t @@ -31,6 +31,6 @@ my $tenv = TestEnvironment->new; $tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; # Test the api using pytest. Prints the test results -run_produces_ok('api pytest suite', ["pytest", "-v"], [], MUST_SUCCEED, 1); +run_produces_ok('api pytest suite', ["pytest-3", "-v"], [], MUST_SUCCEED, 1); -done_testing; \ No newline at end of file +done_testing; From cc4c76b78bed06cdccbe3cf2a13f88de974e538d Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Wed, 15 Apr 2020 05:57:50 -0400 Subject: [PATCH 040/529] Adding python3-pytest to CI dependencies --- .travis.yml | 7 +++++-- api/api.py | 2 ++ t/200-api.t | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a17b3f03..ecff18fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,15 @@ language: python os: - linux +dist: + - bionic + python: - "3.6" before_install: - - sudo apt-get -y install libdb-dev exuberant-ctags - - pip install jinja2 pygments bsddb3 falcon + - sudo apt-get -y install libdb-dev exuberant-ctags python3-pytest + - pip install jinja2 pygments bsddb3 falcon script: - prove \ No newline at end of file diff --git a/api/api.py b/api/api.py index 9ccc5973..55b07d40 100755 --- a/api/api.py +++ b/api/api.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import json import os diff --git a/t/200-api.t b/t/200-api.t index 96d5ad91..289e2265 100644 --- a/t/200-api.t +++ b/t/200-api.t @@ -31,6 +31,6 @@ my $tenv = TestEnvironment->new; $tenv->build_repo(sibling_abs_path('tree'))->build_db->update_env; # Test the api using pytest. Prints the test results -run_produces_ok('api pytest suite', ["pytest-3", "-v"], [], MUST_SUCCEED, 1); +run_produces_ok('api pytest suite', ["pytest", "-v"], [], MUST_SUCCEED, 1); done_testing; From a62164a95feb96ffc43ce6fc534daa469906a597 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Sat, 18 Apr 2020 10:46:44 -0400 Subject: [PATCH 041/529] Converted MD to AsciiDoc --- README.md => README.adoc | 277 ++++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 135 deletions(-) rename README.md => README.adoc (71%) diff --git a/README.md b/README.adoc similarity index 71% rename from README.md rename to README.adoc index 0a443236..d921130d 100644 --- a/README.md +++ b/README.adoc @@ -1,22 +1,25 @@ -[![Build Status](https://travis-ci.com/bootlin/elixir.svg?branch=master)](https://travis-ci.com/bootlin/elixir) +:doctype: book +:pp: {plus}{plus} -# The Elixir Cross Referencer +image::https://travis-ci.com/bootlin/elixir.svg?branch=master[Build Status,link=https://travis-ci.com/bootlin/elixir] + += The Elixir Cross Referencer Elixir is a source code cross-referencer inspired by -[LXR](https://en.wikipedia.org/wiki/LXR_Cross_Referencer). It's written -in Python and its main purpose is to index every release of a C or C++ +https://en.wikipedia.org/wiki/LXR_Cross_Referencer[LXR]. It's written +in Python and its main purpose is to index every release of a C or C{pp} project (like the Linux kernel) while keeping a minimal footprint. It uses Git as a source-code file store and Berkeley DB for cross-reference -data. Internally, it indexes Git *blobs* rather than trees of files to avoid +data. Internally, it indexes Git _blobs_ rather than trees of files to avoid duplicating work and data. It has a straightforward data structure (reminiscent of older LXR releases) to keep queries simple and fast. You can see it in action on https://elixir.bootlin.com/ -Note: this documentation applies to version 1.0 of Elixir. +NOTE: this documentation applies to version 1.0 of Elixir. -# Requirements += Requirements * Python >= 3.6 * The Jinja2 and Pygments (>= 2.2) Python libraries @@ -25,19 +28,19 @@ Note: this documentation applies to version 1.0 of Elixir. * Perl (for non-greedy regexes and automated testing) * Falcon and `mod_wsgi` (for the REST api) -# Installation += Installation -## Architecture +== Architecture Elixir has the following architecture: - .---------------.----------------. - | CGI interface | REST interface | - |---------------|----------------. - | Query command | Update command | - |---------------|----------------| - | Shell script | - '--------------------------------' + .---------------.----------------. + | CGI interface | REST interface | + |---------------|----------------. + | Query command | Update command | + |---------------|----------------| + | Shell script | + '--------------------------------' The shell script (`script.sh`) is the lower layer and provides commands to interact with Git and other Unix utilities. The Python commands use @@ -47,45 +50,49 @@ databases (`update.py`). Finally, the CGI interface (`web.py`) and the REST interface (`api.py`) use the query interface to generate HTML pages and to answer REST queries, respectively. - When installing the system, you should test each layer manually and make sure it works correctly before moving on to the next one. -## Install Manually +== Install Manually -### Install Dependences +=== Install Dependences -> For RedHat/CentOS +____ +For RedHat/CentOS +____ -``` +---- yum install python36-jinja2 python36-pygments python36-bsddb3 python3-falcon global-ctags git httpd -``` -> For Debian +---- + +____ +For Debian +____ -``` +---- sudo apt install python3 python3-jinja2 python3-pygments python3-bsddb3 python3-falcon python3-pytest exuberant-ctags perl git apache2 libapache2-mod-wsgi-py3 -``` +---- -To enable the REST api, follow the installation instructions on [`mod_wsgi`](https://github.com/GrahamDumpleton/mod_wsgi) -and connect it to the apache installation as detailed in . +To enable the REST api, follow the installation instructions on https://github.com/GrahamDumpleton/mod_wsgi[`mod_wsgi`] +and connect it to the apache installation as detailed in https://github.com/GrahamDumpleton/mod_wsgi#connecting-into-apache-installation. To know which packages to install, you can also read the Docker files in the `docker/` directory to know what packages Elixir needs in your favorite distribution. -### Download Elixir Project +=== Download Elixir Project -``` +---- git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ -``` +---- -### Create Directory +=== Create Directory -``` +---- mkdir -p /path/elixir-data/linux/repo mkdir -p /path/elixir-data/linux/data -``` +---- -### Set environment variables +=== Set environment variables Two environment variables are used to tell Elixir where to find the project's local git repository and its databases: @@ -95,58 +102,59 @@ local git repository and its databases: Now open `/etc/profile` and append the following content. -``` +---- export LXR_REPO_DIR=/path/elixir-data/linux/repo export LXR_DATA_DIR=/path/elixir-data/linux/data -``` +---- + And then run `source /etc/profile`. -### Clone Kernel source code +=== Clone Kernel source code First clone the master tree released by Linus Torvalds: -``` +---- cd /pathy/elixir-data/linux git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git repo -``` +---- Then, you should also declare a `stable` remote branch corresponding to the `stable` tree, to get all release updates: -``` +---- cd repo git remote add stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git git fetch stable -``` +---- Feel free to add more remote branches in this way, as Elixir will consider tags from all remote branches. -### First Test +=== First Test -``` +---- cd /usr/local/elixir/ ./script.sh list-tags -``` +---- -### Create Database +=== Create Database -``` +---- ./update.py -``` +---- -> Generating the full database can take a long time: it takes about 15 hours on a Xeon E3-1245 v5 to index 1800 tags in the Linux kernel. For that reason, you may want to tweak the script (for example, by limiting the number of tags with a "head") in order to test the update and query commands. You can even create a new Git repository and just create one tag instead of using the official kernel repository which is very large. +____ +Generating the full database can take a long time: it takes about 15 hours on a Xeon E3-1245 v5 to index 1800 tags in the Linux kernel. For that reason, you may want to tweak the script (for example, by limiting the number of tags with a "head") in order to test the update and query commands. You can even create a new Git repository and just create one tag instead of using the official kernel repository which is very large. +____ -### Second Test +=== Second Test Verify that the queries work: -``` -$ ./query.py v4.10 ident raw_spin_unlock_irq -$ ./query.py v4.10 file /kernel/sched/clock.c -``` + $ ./query.py v4.10 ident raw_spin_unlock_irq + $ ./query.py v4.10 file /kernel/sched/clock.c -Note: `v4.10` can be replaced with any other tag. +NOTE: `v4.10` can be replaced with any other tag. -### Configure httpd +=== Configure httpd The CGI interface (`web.py`) is meant to be called from your web server. Since it includes support for indexing multiple projects, @@ -154,15 +162,15 @@ it expects a different variable (`LXR_PROJ_DIR`) which points to a directory with a specific structure: * `` - * `` - * `data` - * `repo` - * `` - * `data` - * `repo` - * `` - * `data` - * `repo` + ** `` + *** `data` + *** `repo` + ** `` + *** `data` + *** `repo` + ** `` + *** `data` + *** `repo` It will then generate the other two variables upon calling the query command. @@ -171,7 +179,7 @@ Now open `/etc/httpd/conf.d/elixir.conf` and write the following content. Note: If using apache2 (Ubuntu/Debian) instead of httpd (RedHat/Centos), the default config file to edit is: `/etc/apache2/sites-enabled/000-default.conf` -``` +---- HttpProtocolOptions Unsafe # Required for HTTP @@ -203,47 +211,48 @@ AddHandler cgi-script .py RewriteRule "^/$" "/linux/latest/source" [R] RewriteRule "^/(?!api).*/(source|ident|search)" "/web.py" [PT] -``` +---- cgi and rewrite support has been enabled by default in RHEL/CentOS, but you should enable it manually if your distribution is Debian/Ubuntu. -``` +---- a2enmod cgi rewrite -``` +---- Finally, start the httpd server. -``` +---- systemctl start httpd -``` +---- -### Configure lighthttpd +=== Configure lighthttpd Here's a sample configuration for lighthttpd: -``` + +---- server.document-root = server_root + "/elixir/http" url.redirect = ( "^/$" => "/linux/latest/source" ) url.rewrite = ( "^/.*/(source|ident|search)" => "/web.py/$1") setenv.add-environment = ( "PYTHONIOENCODING" => "utf-8", "LXR_PROJ_DIR" => "/path/to/elixir-data" ) -``` +---- -### Using a cache to improve performance +=== Using a cache to improve performance -At Bootlin, we're using the [Varnish http cache](https://varnish-cache.org/) +At Bootlin, we're using the https://varnish-cache.org/[Varnish http cache] as a front-end to reduce the load on the server running the Elixir code. - .-------------. .---------------. .-----------------------. - | Http client | --------> | Varnish cache | --------> | Apache running Elixir | - '-------------' '---------------' '-----------------------' + .-------------. .---------------. .-----------------------. + | Http client | --------> | Varnish cache | --------> | Apache running Elixir | + '-------------' '---------------' '-----------------------' -### Keeping Elixir databases up to date +=== Keeping Elixir databases up to date To keep your Elixir databases up to date and index new versions that are released, we're proposing to use a script like `utils/update-elixir-data` which is called through a daily cron job. -### Keeping git repository disk usage under control +=== Keeping git repository disk usage under control As you keep updating your git repositories, you may notice that some can become considerably bigger than they originally were. This seems to happen when a `gc.log` @@ -252,12 +261,13 @@ to fail, and therefore causing the repository to consume disk space at a fast pace every time new objects are fetched. When this happens, you can save disk space by packing git directories as follows: -``` + +---- cd git prune rm gc.log git gc --aggressive -``` +---- Actually, a second pass with the above commands will save even more space. @@ -265,23 +275,23 @@ To process multiple git repositories in a loop, you may use the `utils/pack-repositories` that we are providing, run from the directory where all repositories are found. -## Building Docker images +== Building Docker images Docker files are provided in the `docker/` directory. To generate your own Docker image for indexing the sources of a project (for example for the Musl project which is much faster to index that Linux), download the `Dockerfile` file for your target distribution and run: - $ docker build -t elixir --build-arg GIT_REPO_URL=git://git.musl-libc.org/musl --build-arg PROJECT=musl . + $ docker build -t elixir --build-arg GIT_REPO_URL=git://git.musl-libc.org/musl --build-arg PROJECT=musl . Then you can use your new container as follows (you get the container id from the output of `docker build`): - $ docker run + $ docker run You can the open the below URL in a browser on your host: http://172.17.0.2/musl/latest/source (change the container IP address if you don't get the default one) -# Database design += Database design `./update.py` stores a bidirectionnal mapping between git object hashes ("blobs") and a sequential key. The goal of indexing such hashes is to reduce their storage footprint (20 bytes for a SHA-1 hash @@ -289,7 +299,7 @@ versus 4 bytes for a 32 bit integer). A detailed diagram of the databases will be provided. Until then, just use the Source, Luke. -# Hardware requirements += Hardware requirements Performance requirements depend mostly on the amount of traffic that you get on your Elixir service. However, a fast server also helps for the initial @@ -301,12 +311,12 @@ git repositories. At Bootlin, here are a few details about the server we're using: * As of July 2019, our Elixir service consumes 17 GB of data (supporting all projects), - or for the Linux kernel alone (version 5.2 being the latest), 12 GB for indexing data, - and 2 GB for the git repository. +or for the Linux kernel alone (version 5.2 being the latest), 12 GB for indexing data, +and 2 GB for the git repository. * We're using an LXD instance with 8 GB of RAM on a cloud server with 8 CPU cores - running at 3.1 GHz. +running at 3.1 GHz. -# Supporting a new project += Supporting a new project Elixir has a very simple modular architecture that allows to support new source code projects by just adding a new file to the Elixir sources. @@ -315,7 +325,7 @@ Elixir's assumptions: * Project sources have to be available in a git repository * All project releases are associated to a given git tag. Elixir - only considers such tags. +only considers such tags. First make an installation of Elixir by following the above instructions. See the `projects` subdirectory for projects that are already supported. @@ -323,8 +333,8 @@ See the `projects` subdirectory for projects that are already supported. Once Elixir works for at least one project, it's time to clone the git repository for the project you want to support: - cd /srv/git - git clone --bare https://github.com/zephyrproject-rtos/zephyr + cd /srv/git + git clone --bare https://github.com/zephyrproject-rtos/zephyr After doing this, you may also reference and fetch remote branches for this project, for example corresponding to the `stable` tree for the Linux kernel (see the @@ -333,16 +343,16 @@ instructions for Linux earlier in this document). Now, in your `LXR_PROJ_DIR` directory, create a new directory for the new project: - cd $LXR_PROJ_DIR - mkdir -p zephyr/data - ln -s /srv/git/zephyr.git repo - export LXR_DATA_DIR=$LXR_PROJ_DIR/data - export LXR_REPO_DIR=$LXR_PROJ_DIR/repo + cd $LXR_PROJ_DIR + mkdir -p zephyr/data + ln -s /srv/git/zephyr.git repo + export LXR_DATA_DIR=$LXR_PROJ_DIR/data + export LXR_REPO_DIR=$LXR_PROJ_DIR/repo Now, go back to the Elixir sources and test that tags are correctly extracted: - ./script.sh list-tags + ./script.sh list-tags Depending on how you want to show the available versions on the Elixir pages, you may have to apply substitutions to each tag string, for example to add @@ -351,13 +361,13 @@ shown. You may also decide to ignore specific tags. All this can be done by redefining the default `list_tags()` function in a new `project/.sh` file. Here's an example (`projects/zephyr.sh` file): - list_tags() - { - echo "$tags" | - grep -v '^zephyr-v' - } + list_tags() + { + echo "$tags" | + grep -v '^zephyr-v' + } -Note that `` **must** match the name of the directory that +Note that `` *must* match the name of the directory that you created under `LXR_PROJ_DIR`. The next step is to make sure that versions are classified as you wish @@ -365,15 +375,15 @@ in the version menu. This classification work is done through the `list_tags_h()` function which generates the output of the `./scripts.sh list-tags -h` command. Here's what you get for the Linux project: - v4 v4.16 v4.16 - v4 v4.16 v4.16-rc7 - v4 v4.16 v4.16-rc6 - v4 v4.16 v4.16-rc5 - v4 v4.16 v4.16-rc4 - v4 v4.16 v4.16-rc3 - v4 v4.16 v4.16-rc2 - v4 v4.16 v4.16-rc1 - ... + v4 v4.16 v4.16 + v4 v4.16 v4.16-rc7 + v4 v4.16 v4.16-rc6 + v4 v4.16 v4.16-rc5 + v4 v4.16 v4.16-rc4 + v4 v4.16 v4.16-rc3 + v4 v4.16 v4.16-rc2 + v4 v4.16 v4.16-rc1 + ... The first column is the top level menu entry for versions. The second one is the next level menu entry, and @@ -387,58 +397,55 @@ to customize the `list_tags_h` function. You should also make sure that Elixir properly identifies the most recent versions: - ./script.sh get-latest + ./script.sh get-latest If needed, customize the `get_latest()` function. You are now ready to generate Elixir's database for your new project: - ./update.py + ./update.py You can then check that Elixir works through your http server. -# REST api usage += REST api usage + After configuring httpd, you can test the api usage: -## ident query +== ident query -Send a get request to ```/api/ident//?version=```. +Send a get request to `/api/ident//?version=`. For example: - curl http://127.0.0.1/api/ident/barebox/cdev?version=latest + curl http://127.0.0.1/api/ident/barebox/cdev?version=latest The response body is of the following structure: -``` + +---- { "definitions": [{"path": "commands/loadb.c", "line": 71, "type": "variable"}, ...], "references": [{"path": "arch/arm/boards/cm-fx6/board.c", "line": "64,64,71,72,75", "type": null}, ...] } -``` +---- -# Automated testing += Automated testing Elixir includes a simple test suite in `t/`. To run it, from the top-level Elixir directory, run: - prove + prove The test suite uses code extracted from Linux v5.4 in `t/tree`. -## Licensing of code in `t/tree` +== Licensing of code in `t/tree` -The copied code is licensed as described in the [COPYING] file included with +The copied code is licensed as described in the https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/COPYING[COPYING] file included with Linux. All the files copied carry SPDX license identifiers of `GPL-2.0+` or -`GPL-2.0-or-later`. Per [GNU's compatibility table], GPL 2.0+ code can be used -under GPLv3 provided the combination is under GPLv3. Moreover, [GNU's overview +`GPL-2.0-or-later`. Per https://www.gnu.org/licenses/gpl-faq.en.html#AllCompatibility[GNU's compatibility table], GPL 2.0+ code can be used +under GPLv3 provided the combination is under GPLv3. Moreover, https://www.gnu.org/licenses/license-list.en.html#AGPLv3.0[GNU's overview of AGPLv3] indicates that its terms "effectively consist of the terms of GPLv3" plus the network-use paragraph. Therefore, the developers have a good-faith -belief that licensing these files under AGPLv3 is authorized. (See also [this +belief that licensing these files under AGPLv3 is authorized. (See also https://github.com/Freemius/wordpress-sdk/issues/166#issuecomment-310561976[this issue comment] for another example of a similar situation.) - -[COPYING]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/COPYING -[GNU's compatibility table]: https://www.gnu.org/licenses/gpl-faq.en.html#AllCompatibility -[GNU's overview of AGPLv3]: https://www.gnu.org/licenses/license-list.en.html#AGPLv3.0 -[this issue comment]: https://github.com/Freemius/wordpress-sdk/issues/166#issuecomment-310561976 From b3ec8a87a19865e110259f5b34d700acfcc2e31d Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Sat, 18 Apr 2020 10:52:53 -0400 Subject: [PATCH 042/529] Added table of contents --- README.adoc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index d921130d..9f03e42b 100644 --- a/README.adoc +++ b/README.adoc @@ -1,10 +1,11 @@ += The Elixir Cross Referencer :doctype: book :pp: {plus}{plus} +:toc: +:toc-placement!: image::https://travis-ci.com/bootlin/elixir.svg?branch=master[Build Status,link=https://travis-ci.com/bootlin/elixir] -= The Elixir Cross Referencer - Elixir is a source code cross-referencer inspired by https://en.wikipedia.org/wiki/LXR_Cross_Referencer[LXR]. It's written in Python and its main purpose is to index every release of a C or C{pp} @@ -19,6 +20,8 @@ You can see it in action on https://elixir.bootlin.com/ NOTE: this documentation applies to version 1.0 of Elixir. +toc::[] + = Requirements * Python >= 3.6 From c9db61ccc2b2e72fb35611b5659636c3862e87c4 Mon Sep 17 00:00:00 2001 From: Carmeli Tamir Date: Sat, 18 Apr 2020 11:12:02 -0400 Subject: [PATCH 043/529] Reordered sections --- README.adoc | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/README.adoc b/README.adoc index 9f03e42b..292bdd60 100644 --- a/README.adoc +++ b/README.adoc @@ -31,9 +31,7 @@ toc::[] * Perl (for non-greedy regexes and automated testing) * Falcon and `mod_wsgi` (for the REST api) -= Installation - -== Architecture += Architecture Elixir has the following architecture: @@ -56,9 +54,17 @@ pages and to answer REST queries, respectively. When installing the system, you should test each layer manually and make sure it works correctly before moving on to the next one. -== Install Manually +== Database design + +`./update.py` stores a bidirectionnal mapping between git object hashes ("blobs") and a sequential key. +The goal of indexing such hashes is to reduce their storage footprint (20 bytes for a SHA-1 hash +versus 4 bytes for a 32 bit integer). + +A detailed diagram of the databases will be provided. Until then, just use the Source, Luke. + += Manual Installation -=== Install Dependences +== Install Dependences ____ For RedHat/CentOS @@ -82,20 +88,20 @@ and connect it to the apache installation as detailed in https://github.com/Grah To know which packages to install, you can also read the Docker files in the `docker/` directory to know what packages Elixir needs in your favorite distribution. -=== Download Elixir Project +== Download Elixir Project ---- git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ ---- -=== Create Directory +== Create Directory ---- mkdir -p /path/elixir-data/linux/repo mkdir -p /path/elixir-data/linux/data ---- -=== Set environment variables +== Set environment variables Two environment variables are used to tell Elixir where to find the project's local git repository and its databases: @@ -112,7 +118,7 @@ export LXR_DATA_DIR=/path/elixir-data/linux/data And then run `source /etc/profile`. -=== Clone Kernel source code +== Clone Kernel source code First clone the master tree released by Linus Torvalds: @@ -131,14 +137,14 @@ git fetch stable Feel free to add more remote branches in this way, as Elixir will consider tags from all remote branches. -=== First Test +== First Test ---- cd /usr/local/elixir/ ./script.sh list-tags ---- -=== Create Database +== Create Database ---- ./update.py @@ -148,7 +154,7 @@ ____ Generating the full database can take a long time: it takes about 15 hours on a Xeon E3-1245 v5 to index 1800 tags in the Linux kernel. For that reason, you may want to tweak the script (for example, by limiting the number of tags with a "head") in order to test the update and query commands. You can even create a new Git repository and just create one tag instead of using the official kernel repository which is very large. ____ -=== Second Test +== Second Test Verify that the queries work: @@ -157,7 +163,7 @@ Verify that the queries work: NOTE: `v4.10` can be replaced with any other tag. -=== Configure httpd +== Configure httpd The CGI interface (`web.py`) is meant to be called from your web server. Since it includes support for indexing multiple projects, @@ -228,7 +234,7 @@ Finally, start the httpd server. systemctl start httpd ---- -=== Configure lighthttpd +== Configure lighthttpd Here's a sample configuration for lighthttpd: @@ -240,7 +246,9 @@ setenv.add-environment = ( "PYTHONIOENCODING" => "utf-8", "LXR_PROJ_DIR" => "/path/to/elixir-data" ) ---- -=== Using a cache to improve performance += Maintenance and enhancements + +== Using a cache to improve performance At Bootlin, we're using the https://varnish-cache.org/[Varnish http cache] as a front-end to reduce the load on the server running the Elixir code. @@ -249,13 +257,13 @@ as a front-end to reduce the load on the server running the Elixir code. | Http client | --------> | Varnish cache | --------> | Apache running Elixir | '-------------' '---------------' '-----------------------' -=== Keeping Elixir databases up to date +== Keeping Elixir databases up to date To keep your Elixir databases up to date and index new versions that are released, we're proposing to use a script like `utils/update-elixir-data` which is called through a daily cron job. -=== Keeping git repository disk usage under control +== Keeping git repository disk usage under control As you keep updating your git repositories, you may notice that some can become considerably bigger than they originally were. This seems to happen when a `gc.log` @@ -278,7 +286,7 @@ To process multiple git repositories in a loop, you may use the `utils/pack-repositories` that we are providing, run from the directory where all repositories are found. -== Building Docker images += Building Docker images Docker files are provided in the `docker/` directory. To generate your own Docker image for indexing the sources of a project (for example for the Musl @@ -294,14 +302,6 @@ Then you can use your new container as follows (you get the container id from th You can the open the below URL in a browser on your host: http://172.17.0.2/musl/latest/source (change the container IP address if you don't get the default one) -= Database design - -`./update.py` stores a bidirectionnal mapping between git object hashes ("blobs") and a sequential key. -The goal of indexing such hashes is to reduce their storage footprint (20 bytes for a SHA-1 hash -versus 4 bytes for a 32 bit integer). - -A detailed diagram of the databases will be provided. Until then, just use the Source, Luke. - = Hardware requirements Performance requirements depend mostly on the amount of traffic that you get From f597fce20dce3ffe5a422093296a33ee1fedca3c Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 21 Apr 2020 09:47:12 +0200 Subject: [PATCH 044/529] Add contribution guidelines - Coding style - How to send patches Signed-off-by: Michael Opdenacker --- README.adoc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 292bdd60..19c6c89e 100644 --- a/README.adoc +++ b/README.adoc @@ -319,7 +319,9 @@ and 2 GB for the git repository. * We're using an LXD instance with 8 GB of RAM on a cloud server with 8 CPU cores running at 3.1 GHz. -= Supporting a new project += Contributing to Elixir + +== Supporting a new project Elixir has a very simple modular architecture that allows to support new source code projects by just adding a new file to the Elixir sources. @@ -411,6 +413,16 @@ new project: You can then check that Elixir works through your http server. +== Coding style + +If you wish to contribute to Elixir's Python code, please +follow the https://www.python.org/dev/peps/pep-0008/[official coding style for Python]. + +== How to send patches + +The best way to share your contributions with us is to https://github.com/bootlin/elixir/pulls[file a pull +request on GitHub]. + = REST api usage After configuring httpd, you can test the api usage: From f25d1b32059b7cf7f267ef88bb88a893a4792c1b Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Mon, 20 Apr 2020 09:44:44 +0200 Subject: [PATCH 045/529] Add support for the Toybox project Signed-off-by: Maxime Chretien --- projects/toybox.sh | 8 ++++++++ utils/index-all-repositories | 1 + utils/update-elixir-data | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 projects/toybox.sh diff --git a/projects/toybox.sh b/projects/toybox.sh new file mode 100644 index 00000000..92881296 --- /dev/null +++ b/projects/toybox.sh @@ -0,0 +1,8 @@ +# Elixir definitions for Toybox + +list_tags_h() +{ + echo "$tags" | + tac | + sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/\1 \1.\2 \1.\2\3/' +} \ No newline at end of file diff --git a/utils/index-all-repositories b/utils/index-all-repositories index a526019e..d2899575 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -65,3 +65,4 @@ index qemu https://git.qemu.org/git/qemu.git & index u-boot https://gitlab.denx.de/u-boot/u-boot.git & index uclibc-ng git://uclibc-ng.org/git/uclibc-ng & index zephyr https://github.com/zephyrproject-rtos/zephyr & +index toybox https://github.com/landley/toybox.git & diff --git a/utils/update-elixir-data b/utils/update-elixir-data index d1ba65bf..b3557506 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -13,7 +13,7 @@ if [ -z "$ELIXIR_INSTALL" ]; then exit 1 fi -for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono dpdk; do +for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono dpdk toybox; do echo "Processing project $root/$p ..." export LXR_DATA_DIR=$root/$p/data export LXR_REPO_DIR=$root/$p/repo From 24da3e66ddd5f9436f979182b1bd32b13fb47f80 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 21 Apr 2020 11:30:24 +0200 Subject: [PATCH 046/529] Improve REST API documentation - Using "API" instead of "api" - Putting the REST API doc in the right place in the document Signed-off-by: Michael Opdenacker --- README.adoc | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/README.adoc b/README.adoc index 19c6c89e..3807989f 100644 --- a/README.adoc +++ b/README.adoc @@ -29,7 +29,7 @@ toc::[] * Berkeley DB (and its Python binding) * Exuberant Ctags * Perl (for non-greedy regexes and automated testing) -* Falcon and `mod_wsgi` (for the REST api) +* Falcon and `mod_wsgi` (for the REST API) = Architecture @@ -82,7 +82,7 @@ ____ sudo apt install python3 python3-jinja2 python3-pygments python3-bsddb3 python3-falcon python3-pytest exuberant-ctags perl git apache2 libapache2-mod-wsgi-py3 ---- -To enable the REST api, follow the installation instructions on https://github.com/GrahamDumpleton/mod_wsgi[`mod_wsgi`] +To enable the REST API, follow the installation instructions on https://github.com/GrahamDumpleton/mod_wsgi[`mod_wsgi`] and connect it to the apache installation as detailed in https://github.com/GrahamDumpleton/mod_wsgi#connecting-into-apache-installation. To know which packages to install, you can also read the Docker files in the `docker/` directory @@ -246,6 +246,28 @@ setenv.add-environment = ( "PYTHONIOENCODING" => "utf-8", "LXR_PROJ_DIR" => "/path/to/elixir-data" ) ---- += REST API usage + +After configuring httpd, you can test the API usage: + +== ident query + +Send a get request to `/api/ident//?version=`. +For example: + + curl http://127.0.0.1/api/ident/barebox/cdev?version=latest + +The response body is of the following structure: + +---- +{ + "definitions": + [{"path": "commands/loadb.c", "line": 71, "type": "variable"}, ...], + "references": + [{"path": "arch/arm/boards/cm-fx6/board.c", "line": "64,64,71,72,75", "type": null}, ...] +} +---- + = Maintenance and enhancements == Using a cache to improve performance @@ -423,28 +445,6 @@ follow the https://www.python.org/dev/peps/pep-0008/[official coding style for P The best way to share your contributions with us is to https://github.com/bootlin/elixir/pulls[file a pull request on GitHub]. -= REST api usage - -After configuring httpd, you can test the api usage: - -== ident query - -Send a get request to `/api/ident//?version=`. -For example: - - curl http://127.0.0.1/api/ident/barebox/cdev?version=latest - -The response body is of the following structure: - ----- -{ - "definitions": - [{"path": "commands/loadb.c", "line": 71, "type": "variable"}, ...], - "references": - [{"path": "arch/arm/boards/cm-fx6/board.c", "line": "64,64,71,72,75", "type": null}, ...] -} ----- - = Automated testing Elixir includes a simple test suite in `t/`. To run it, From ea0964a76c22787952fdc92cd1eaa52128ca3b39 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Tue, 21 Apr 2020 11:55:30 +0200 Subject: [PATCH 047/529] http/web.py: Remove broken old code If this test succeed it clears cmd so the next test always leads to an error 400. And this code is not used anymore so it's better to remove it. Signed-off-by: Maxime Chretien --- http/web.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/http/web.py b/http/web.py index 55e38c96..4e78fe3f 100755 --- a/http/web.py +++ b/http/web.py @@ -56,13 +56,6 @@ def print(arg, end='\n'): cmd = m.group(3) arg = m.group(4) - # Support old LXR links - if not(project and search('^[A-Za-z0-9-]+$', project)) \ - or not(version and search('^[A-Za-z0-9._-]+$', version)): - status = 302 - location = '/linux/latest/'+cmd+arg - cmd = '' - basedir = os.environ['LXR_PROJ_DIR'] datadir = basedir + '/' + project + '/data' repodir = basedir + '/' + project + '/repo' From 9e9b1580579d5700a32beaca67603d2c23a37e9e Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Mon, 20 Apr 2020 14:14:27 +0200 Subject: [PATCH 048/529] http/web.py: Encode slashes in URL This allow the use of tags with slashes. And thus fix https://github.com/bootlin/elixir/issues/106 Signed-off-by: Maxime Chretien --- http/web.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/http/web.py b/http/web.py index 55e38c96..d4d4ba71 100755 --- a/http/web.py +++ b/http/web.py @@ -53,6 +53,7 @@ def print(arg, end='\n'): if m: project = m.group(1) version = m.group(2) + versionDecoded = parse.unquote(version) cmd = m.group(3) arg = m.group(4) @@ -125,10 +126,10 @@ def print(arg, end='\n'): sys.path = [ sys.path[0] + '/..' ] + sys.path from query import query -if version == 'latest': +if versionDecoded == 'latest': tag = query('latest') else: - tag = version + tag = versionDecoded data = { 'baseurl': '/' + project + '/', @@ -161,8 +162,9 @@ def print(arg, end='\n'): v += '\t\t\t'+submenu+'\n' v += '\t\t\t
    \n' for _tag in tags: - if _tag == tag: v += '\t\t\t\t\n' - else: v += '\t\t\t\t\n' + _tagEncoded = parse.quote(_tag, safe='') + if _tag == tag: v += '\t\t\t\t\n' + else: v += '\t\t\t\t\n' v += '\t\t\t
\n' v += '\t\n' From 2864ae17317de77f737be690b296c40de65ce964 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Tue, 21 Apr 2020 10:43:03 +0200 Subject: [PATCH 049/529] README: Update apache configuration Allow apache to use encoded slashes Signed-off-by: Maxime Chretien --- README.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.adoc b/README.adoc index 3807989f..adf73dca 100644 --- a/README.adoc +++ b/README.adoc @@ -216,6 +216,8 @@ AddHandler cgi-script .py # To enable REST api after installing mod_wsgi: Fill path and uncomment: #WSGIScriptAlias /api /usr/local/elixir/api/api.py + AllowEncodedSlashes On + RewriteEngine on RewriteRule "^/$" "/linux/latest/source" [R] RewriteRule "^/(?!api).*/(source|ident|search)" "/web.py" [PT] From 2815704f9f2f00f863d3e1f9d5d4c0f240dec3eb Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Tue, 21 Apr 2020 16:39:51 +0200 Subject: [PATCH 050/529] http/web.py: Follow coding style Signed-off-by: Maxime Chretien --- http/web.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/http/web.py b/http/web.py index d4d4ba71..5395f931 100755 --- a/http/web.py +++ b/http/web.py @@ -53,7 +53,7 @@ def print(arg, end='\n'): if m: project = m.group(1) version = m.group(2) - versionDecoded = parse.unquote(version) + version_decoded = parse.unquote(version) cmd = m.group(3) arg = m.group(4) @@ -126,10 +126,10 @@ def print(arg, end='\n'): sys.path = [ sys.path[0] + '/..' ] + sys.path from query import query -if versionDecoded == 'latest': +if version_decoded == 'latest': tag = query('latest') else: - tag = versionDecoded + tag = version_decoded data = { 'baseurl': '/' + project + '/', @@ -162,9 +162,9 @@ def print(arg, end='\n'): v += '\t\t\t'+submenu+'\n' v += '\t\t\t
    \n' for _tag in tags: - _tagEncoded = parse.quote(_tag, safe='') - if _tag == tag: v += '\t\t\t\t\n' - else: v += '\t\t\t\t\n' + _tag_encoded = parse.quote(_tag, safe='') + if _tag == tag: v += '\t\t\t\t\n' + else: v += '\t\t\t\t\n' v += '\t\t\t
\n' v += '\t\n' From 7e6f88c26e31e248bdb4d9f700d59b41b4a0eec0 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 23 Apr 2020 13:24:06 +0200 Subject: [PATCH 051/529] Add database performance test script This solves issue https://github.com/bootlin/elixir/issues/109 This script can be used with every project and with any number of versions, files or identifiers. Signed-off-by: Maxime Chretien --- utils/speedtest.py | 149 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 utils/speedtest.py diff --git a/utils/speedtest.py b/utils/speedtest.py new file mode 100755 index 00000000..890e50db --- /dev/null +++ b/utils/speedtest.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +from time import time +import os +import sys + +BOLD = '\033[1m' +NORMAL = '\033[0m' + +ELIXIR_DIR = os.path.dirname(__file__) + '/..' +sys.path = [ ELIXIR_DIR ] + sys.path + +#Parameters +run_number = 100 +verbose = False + +#List of test elements +project = 'linux' +versions = ['latest', 'v5.6.2'] +idents = [ 'loopback', + 'devm_register_reboot_notifier', + 'notrace', + 'arch_local_irq_restore', + 'blk_queue_dma_alignment', + 'spinlock_t', + 'max', + 'task_struct', + 'eth_header', + 'sk_buff' ] + +files = [ '/block/partitions/osf.c', + '/mm/kasan/quarantine.c', + '/include/crypto/internal/hash.h', + '/drivers/gpu/drm/gma500/gma_display.c', + '/drivers/hwmon/pmbus/ltc2978.c', + '/virt/lib/irqbypass.c', + '/Makefile', + '/fs/btrfs/tree-checker.c', + '/kernel/locking/qspinlock_stat.h', + '/arch/arm/boot/dts/armada-375.dtsi' ] + + +#Test results +idents_results = [] +idents_max = 0 +idents_min = 0 +idents_average = 0 + +files_results = [] +files_max = 0 +files_min = 0 +files_average = 0 + + +def init_query(project): + if 'LXR_PROJ_DIR' not in os.environ: + print("ERROR : LXR_PROJ_DIR not defined !") + return None; + + basedir = os.environ['LXR_PROJ_DIR'] + os.environ['LXR_DATA_DIR']= basedir + '/' + project + '/data' + os.environ['LXR_REPO_DIR'] = basedir + '/' + project + '/repo' + + import query + return query.query + + +def get_ident(ident, version): + if version == 'latest': + version = query('latest') + + return query('ident', version, ident) + +def get_file(path, version): + if version == 'latest': + version = query('latest') + + return query('file', version, path) + + +#Read arguments +if(len(sys.argv) > 1): + if(sys.argv[1] == '-v'): + verbose = True + elif (sys.argv[1] == '-h'): + print("LXR_PROJ_DIR needs to be set before launching this script\n" + + "Options :\n" + + "-v Verbose mode (Show requests details)") + exit() + +#Query init +query = init_query(project) +if(query == None): + exit() + +#Database test +print((BOLD + "Database test for project {}" + NORMAL).format(project)) + +print("Each test runs {} times".format(run_number)) + +for version in versions: + print((BOLD + "\nVersion tested : {}\n" + NORMAL).format(version)) + + print(BOLD + "Identifiers access test\n" + NORMAL) + for i in range(run_number): + for ident in idents: + start_time = time() + get_ident(ident, version) + end_time = time() + + elapsed_time = (end_time - start_time)*1000 #convert to ms + idents_results.append(elapsed_time) + + if verbose: + print("Identifier : {}".format(ident)) + print("Elapsed time : {0:.6f} ms\n".format(elapsed_time)) + + + idents_min = min(idents_results) + idents_max = max(idents_results) + idents_average = sum(idents_results)/len(idents_results) + + print((BOLD + "Min:" + NORMAL + " {0:.6f} ms\n" + + BOLD + "Max:" + NORMAL + " {1:.6f} ms\n" + + BOLD + "Average:" + NORMAL + " {2:.6f} ms\n" + ).format(idents_min, idents_max, idents_average)) + + print(BOLD + "Files access test\n" + NORMAL) + for i in range(run_number): + for file in files: + start_time = time() + get_file(file, version) + end_time = time() + + elapsed_time = (end_time - start_time) * 1000 #convert to ms + files_results.append(elapsed_time) + + if verbose: + print("File : {}".format(file)) + print("Elapsed time : {0:.6f} ms\n".format(elapsed_time)) + + + files_min = min(files_results) + files_max = max(files_results) + files_average = sum(files_results)/len(files_results) + + print((BOLD + "Min:" + NORMAL + " {0:.6f} ms\n" + + BOLD + "Max:" + NORMAL + " {1:.6f} ms\n" + + BOLD + "Average:" + NORMAL + " {2:.6f} ms\n" + ).format(files_min, files_max, files_average)) \ No newline at end of file From 8988f34721167d907dae5d29c33e012418593fb6 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 23 Apr 2020 14:00:30 +0200 Subject: [PATCH 052/529] script.sh: Specify current dir when including specific project scripts This fix the FIXME tag name in the web menus Signed-off-by: Maxime Chretien --- script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script.sh b/script.sh index 16002687..80f80048 100755 --- a/script.sh +++ b/script.sh @@ -159,7 +159,7 @@ parse_docs() project=$(basename `dirname $LXR_REPO_DIR`) -plugin=projects/$project.sh +plugin=$script_dir/projects/$project.sh if [ -f "$plugin" ] ; then . $plugin fi From a73cfcc3d02a6fa0b1882906966ebaa46c7fae79 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 24 Apr 2020 15:47:31 +0200 Subject: [PATCH 053/529] Add details about DTS syntax highlighting Signed-off-by: Michael Opdenacker --- README.adoc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.adoc b/README.adoc index adf73dca..afda117e 100644 --- a/README.adoc +++ b/README.adoc @@ -88,6 +88,24 @@ and connect it to the apache installation as detailed in https://github.com/Grah To know which packages to install, you can also read the Docker files in the `docker/` directory to know what packages Elixir needs in your favorite distribution. +== Special dependencies + +=== Device Tree Source syntax highlighting + +The service on https://elixir.bootlin.com relies on a modified version of https://pygments.org/[Pygments], +to enable syntax highlighting on Device Tree Source (DTS) files. + +The changes have been sent upstream and should appear in future distributions. + +In the meantime, you can either rebuild this package from its source on +https://github.com/MaximeChretien/pygments/tree/devicetree-lexer[GitHub], or download this +https://bootlin.com/pub/elixir/Pygments-2.6.1.devicetree-py3-none-any.whl[binary module] +and install it as follows: + +---- +sudo pip3-install Pygments-2.6.1.devicetree-py3-none-any.whl +---- + == Download Elixir Project ---- From a03d0abdc3b64ba814bf6efec9035db222004ac7 Mon Sep 17 00:00:00 2001 From: llccd Date: Mon, 27 Apr 2020 17:01:33 +0800 Subject: [PATCH 054/529] Fix a typo in pack-repositories --- utils/pack-repositories | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/pack-repositories b/utils/pack-repositories index 18dc0c28..c60bd40b 100755 --- a/utils/pack-repositories +++ b/utils/pack-repositories @@ -1,7 +1,7 @@ #!/bin/sh # # Goes through all git repositories in the current directory -# and runs "git prune" and "git gcc --aggressive" to pack +# and runs "git prune" and "git gc --aggressive" to pack # objects very efficiently and reduce size sometimes dramatically # # Works by finding the repositories which have a gc.log From 2938221b995b4666aef1f0edf1994ea24e64abb4 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 13 May 2020 10:50:22 +0200 Subject: [PATCH 055/529] filters/makefiledir.py : Fix wrong text added as dir This fix different problems : - http://elixir.bootlin.com/linux/v5.6.4/source/Makefile#L334 Here "scripts/" was changed to a link to /scripts/Makefile but this file does not exists - https://elixir.bootlin.com/linux/v5.6.12/source/init/Makefile#L24 Here "include/generated/compile.h" was changed to __KEEPMAKEFILEDIR__1/__KEEPMAKEFILEDIR__2/compile.h - https://elixir.bootlin.com/linux/v5.6.12/source/Makefile#L619 Here the link was created as "//init/Makefile" so it did not work This maybe fix other problems I haven't see Signed-off-by: Maxime Chretien --- http/filters/makefiledir.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/http/filters/makefiledir.py b/http/filters/makefiledir.py index 9a357e94..4677266a 100644 --- a/http/filters/makefiledir.py +++ b/http/filters/makefiledir.py @@ -9,12 +9,17 @@ def keep_makefiledir(m): def replace_makefiledir(m): w = makefiledir[int(m.group(1)) - 1] - return ''+w+'/' + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + return ''+w+'/' makefiledir_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '([-\w]+)/(\s*|$)', + 'prerex': '([-\w]+)/(\s+|$)', 'prefunc': keep_makefiledir, 'postrex': '__KEEPMAKEFILEDIR__(\d+)/', 'postfunc': replace_makefiledir From 3d740c0081444e12f9ae612bdadddd96863e6188 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 13 May 2020 11:55:15 +0200 Subject: [PATCH 056/529] filters/makefileo.py: Fix file extensions added as .o Files with extensions stating with a o were wrongly added as .o Example : "modules.order" https://elixir.bootlin.com/linux/v5.6.12/source/Makefile#L1013 Signed-off-by: Maxime Chretien --- http/filters/makefileo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/filters/makefileo.py b/http/filters/makefileo.py index 86a7b75e..70866acf 100644 --- a/http/filters/makefileo.py +++ b/http/filters/makefileo.py @@ -14,7 +14,7 @@ def replace_makefileo(m): makefileo_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '([-\w]+)\.o', + 'prerex': '([-\w]+)\.o(?!\w)', 'prefunc': keep_makefileo, 'postrex': '__KEEPMAKEFILEO__(\d+)\.o', 'postfunc': replace_makefileo From 61de6ad15bcf92298be9ae4431308bdc86f907c8 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 13 May 2020 13:42:36 +0200 Subject: [PATCH 057/529] filters/makefile* : Fix root path problem Apply this fix on all makefile filters Signed-off-by: Maxime Chretien --- http/filters/makefiledtb.py | 8 +++++++- http/filters/makefileo.py | 8 +++++++- http/filters/makefilesubdir.py | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/http/filters/makefiledtb.py b/http/filters/makefiledtb.py index cca938b7..0ee0eaba 100644 --- a/http/filters/makefiledtb.py +++ b/http/filters/makefiledtb.py @@ -9,7 +9,13 @@ def keep_makefiledtb(m): def replace_makefiledtb(m): w = makefiledtb[int(m.group(1)) - 1] - return ''+w+'.dtb' + + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + return ''+w+'.dtb' makefiledtb_filters = { 'case': 'filename', diff --git a/http/filters/makefileo.py b/http/filters/makefileo.py index 70866acf..9a9510b5 100644 --- a/http/filters/makefileo.py +++ b/http/filters/makefileo.py @@ -9,7 +9,13 @@ def keep_makefileo(m): def replace_makefileo(m): w = makefileo[int(m.group(1)) - 1] - return ''+w+'.o' + + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + return ''+w+'.o' makefileo_filters = { 'case': 'filename', diff --git a/http/filters/makefilesubdir.py b/http/filters/makefilesubdir.py index ee171c5e..56d5abe8 100644 --- a/http/filters/makefilesubdir.py +++ b/http/filters/makefilesubdir.py @@ -9,7 +9,13 @@ def keep_makefilesubdir(m): def replace_makefilesubdir(m): w = makefilesubdir[int(m.group(1)) - 1] - return ''+w+'' + + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + return ''+w+'' makefilesubdir_filters = { 'case': 'filename', From f726b1526a0e491af3f8665e3f3f9560feced687 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 13 May 2020 13:45:31 +0200 Subject: [PATCH 058/529] filters/makefile* : Allow subdirectories Sometimes directories or files are declared this way : obj-y += drivers/power/domain/ lib/efi/efi_stub.o So we need to take in account all the directories of the given path when creating the link Signed-off-by: Maxime Chretien --- http/filters/makefiledir.py | 2 +- http/filters/makefiledtb.py | 2 +- http/filters/makefileo.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/http/filters/makefiledir.py b/http/filters/makefiledir.py index 4677266a..34322ad7 100644 --- a/http/filters/makefiledir.py +++ b/http/filters/makefiledir.py @@ -19,7 +19,7 @@ def replace_makefiledir(m): makefiledir_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '([-\w]+)/(\s+|$)', + 'prerex': '(?<=\s)([-\w/]+)/(\s+|$)', 'prefunc': keep_makefiledir, 'postrex': '__KEEPMAKEFILEDIR__(\d+)/', 'postfunc': replace_makefiledir diff --git a/http/filters/makefiledtb.py b/http/filters/makefiledtb.py index 0ee0eaba..0971c88f 100644 --- a/http/filters/makefiledtb.py +++ b/http/filters/makefiledtb.py @@ -20,7 +20,7 @@ def replace_makefiledtb(m): makefiledtb_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '([-\w]+)\.dtb', + 'prerex': '(?<=\s)([-\w/]+)\.dtb', 'prefunc': keep_makefiledtb, 'postrex': '__KEEPMAKEFILEDTB__(\d+)\.dtb', 'postfunc': replace_makefiledtb diff --git a/http/filters/makefileo.py b/http/filters/makefileo.py index 9a9510b5..bf557cd0 100644 --- a/http/filters/makefileo.py +++ b/http/filters/makefileo.py @@ -20,7 +20,7 @@ def replace_makefileo(m): makefileo_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '([-\w]+)\.o(?!\w)', + 'prerex': '(?<=\s)([-\w/]+)\.o(?!\w)', 'prefunc': keep_makefileo, 'postrex': '__KEEPMAKEFILEO__(\d+)\.o', 'postfunc': replace_makefileo From c14844e03ec9ce9407ceb4535de7435567183f26 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 13 May 2020 14:40:10 +0200 Subject: [PATCH 059/529] filters : Add filter for file listed in Makefiles Signed-off-by: Maxime Chretien --- http/filters/linux.py | 1 + http/filters/makefilefile.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 http/filters/makefilefile.py diff --git a/http/filters/linux.py b/http/filters/linux.py index c1767efa..3aa6ccfa 100644 --- a/http/filters/linux.py +++ b/http/filters/linux.py @@ -5,6 +5,7 @@ exec(open('makefileo.py').read()) exec(open('makefiledtb.py').read()) exec(open('makefiledir.py').read()) +exec(open('makefilefile.py').read()) exec(open('makefilesubdir.py').read()) exec(open('cpppathinc.py').read()) diff --git a/http/filters/makefilefile.py b/http/filters/makefilefile.py new file mode 100644 index 00000000..058ad3a5 --- /dev/null +++ b/http/filters/makefilefile.py @@ -0,0 +1,27 @@ +# Filters for files listed in Makefiles + +makefilefile = [] + +def keep_makefilefile(m): + makefilefile.append(m.group(1)) + return '__KEEPMAKEFILEFILE__' + str(len(makefilefile)) + m.group(2) + +def replace_makefilefile(m): + w = makefilefile[int(m.group(1)) - 1] + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + return ''+w+'' + +makefilefile_filters = { + 'case': 'filename', + 'match': {'Makefile'}, + 'prerex': '(?<=\s)(?!/)([-\w/]+/[-\w\.]+)(\s+|$)', + 'prefunc': keep_makefilefile, + 'postrex': '__KEEPMAKEFILEFILE__(\d+)', + 'postfunc': replace_makefilefile + } + +filters.append(makefilefile_filters) From ac491ab3f88185b7d2ceb16bcc4618712c7690ac Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 13 May 2020 15:53:23 +0200 Subject: [PATCH 060/529] filters/makefilefile.py : Make sure the file or directory exists before creating a link Signed-off-by: Maxime Chretien --- http/filters/makefilefile.py | 12 ++++++++++-- query.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/http/filters/makefilefile.py b/http/filters/makefilefile.py index 058ad3a5..0aedd56f 100644 --- a/http/filters/makefilefile.py +++ b/http/filters/makefilefile.py @@ -3,8 +3,16 @@ makefilefile = [] def keep_makefilefile(m): - makefilefile.append(m.group(1)) - return '__KEEPMAKEFILEFILE__' + str(len(makefilefile)) + m.group(2) + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + if query('exist', tag, dir_name + m.group(1)): + makefilefile.append(m.group(1)) + return '__KEEPMAKEFILEFILE__' + str(len(makefilefile)) + m.group(2) + else: + return m.group(0) def replace_makefilefile(m): w = makefilefile[int(m.group(1)) - 1] diff --git a/query.py b/query.py index 1825e370..6a744a3f 100755 --- a/query.py +++ b/query.py @@ -103,6 +103,24 @@ def query(cmd, *args): path = args[1] return decode(script('get-type', version, path)).strip() + elif cmd == 'exist': + + # Returns True if the requested file exists, overwise returns False + + version = args[0] + path = args[1] + + dirname, filename = os.path.split(path) + + entries = decode(script('get-dir', version, dirname)).split("\n")[:-1] + for entry in entries: + fname = entry.split(" ")[1] + + if fname == filename: + return True + + return False + elif cmd == 'dir': # Returns the contents (trees or blobs) of the specified directory From b80dd681ecab3595cb6dcad20b0648e95b1e38eb Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 14 May 2020 10:06:33 +0200 Subject: [PATCH 061/529] filters: Encode numbers into letters to prevent pygments to modify the string This prevent pygments from adding a between __KEEPMAKEFILEDIR__ and the following number like __KEEPMAKEFILEDIR__12 Signed-off-by: Maxime Chretien --- http/filters/common.py | 26 ++++++++++++++++++++++++++ http/filters/configin.py | 6 +++--- http/filters/cppinc.py | 6 +++--- http/filters/cpppathinc.py | 6 +++--- http/filters/dtsi.py | 6 +++--- http/filters/ident.py | 6 +++--- http/filters/kconfig.py | 6 +++--- http/filters/makefiledir.py | 6 +++--- http/filters/makefiledtb.py | 6 +++--- http/filters/makefilefile.py | 6 +++--- http/filters/makefileo.py | 6 +++--- http/filters/makefilesubdir.py | 6 +++--- 12 files changed, 59 insertions(+), 33 deletions(-) diff --git a/http/filters/common.py b/http/filters/common.py index 0323d53a..ab7ea46d 100644 --- a/http/filters/common.py +++ b/http/filters/common.py @@ -1,5 +1,31 @@ # Common filters +def encode_number(number): + result = '' + + while number != 0: + number, rem = divmod(number, 10) + + rem = chr(ord('A') + rem) + + result = rem + result + + return result + + +def decode_number(string): + result = '' + + while string != '': + string, char = string[:-1], string[-1] + + char = str(ord(char) - ord('A')) + + result = char + result + + return int(result) + + filters = [] exec(open('ident.py').read()) exec(open('cppinc.py').read()) diff --git a/http/filters/configin.py b/http/filters/configin.py index 7463b136..dc3440d4 100644 --- a/http/filters/configin.py +++ b/http/filters/configin.py @@ -4,10 +4,10 @@ def keep_configin(m): configin.append(m.group(4)) - return m.group(1) + m.group(2) + m.group(3) + '"__KEEPCONFIGIN__' + str(len(configin)) + '"' + return m.group(1) + m.group(2) + m.group(3) + '"__KEEPCONFIGIN__' + encode_number(len(configin)) + '"' def replace_configin(m): - w = configin[int(m.group(1)) - 1] + w = configin[decode_number(m.group(1)) - 1] return ''+w+'' configin_filters = { @@ -15,7 +15,7 @@ def replace_configin(m): 'match': {'Config'}, 'prerex': '^(\s*)(source)(\s*)\"(.*)\"', 'prefunc': keep_configin, - 'postrex': '__KEEPCONFIGIN__(\d+)', + 'postrex': '__KEEPCONFIGIN__([A-J]+)', 'postfunc': replace_configin } diff --git a/http/filters/cppinc.py b/http/filters/cppinc.py index e6e9000b..110fb5f2 100644 --- a/http/filters/cppinc.py +++ b/http/filters/cppinc.py @@ -5,10 +5,10 @@ def keep_cppinc(m): cppinc.append(m.group(3)) - return m.group(1) + '#include' + m.group(2) + '"__KEEPCPPINC__' + str(len(cppinc)) + '"' + return m.group(1) + '#include' + m.group(2) + '"__KEEPCPPINC__' + encode_number(len(cppinc)) + '"' def replace_cppinc(m): - w = cppinc[int(m.group(1)) - 1] + w = cppinc[decode_number(m.group(1)) - 1] return ''+w+'' cppinc_filters = { @@ -16,7 +16,7 @@ def replace_cppinc(m): 'match': {'dts', 'dtsi', 'c', 'cc', 'cpp', 'c++', 'cxx', 'h', 's'}, 'prerex': '^(\s*)#include(\s*)\"(.*?)\"', 'prefunc': keep_cppinc, - 'postrex': '__KEEPCPPINC__(\d+)', + 'postrex': '__KEEPCPPINC__([A-J]+)', 'postfunc': replace_cppinc } diff --git a/http/filters/cpppathinc.py b/http/filters/cpppathinc.py index 68344a5a..fb872128 100644 --- a/http/filters/cpppathinc.py +++ b/http/filters/cpppathinc.py @@ -17,10 +17,10 @@ def keep_cpppathinc(m): return m1 + '#include' + m2 + '<' + inc + '>' else: cpppathinc.append(inc) - return m1 + '#include' + m2 + '<__KEEPCPPPATHINC__' + str(len(cpppathinc)) + '>' + return m1 + '#include' + m2 + '<__KEEPCPPPATHINC__' + encode_number(len(cpppathinc)) + '>' def replace_cpppathinc(m): - w = cpppathinc[int(m.group(1)) - 1] + w = cpppathinc[decode_number(m.group(1)) - 1] return ''+w+'' cpppathinc_filters = { @@ -28,7 +28,7 @@ def replace_cpppathinc(m): 'match': {'dts', 'dtsi', 'c', 'cc', 'cpp', 'c++', 'cxx', 'h', 's'}, 'prerex': '^(\s*)#include(\s*)<(.*?)>', 'prefunc': keep_cpppathinc, - 'postrex': '__KEEPCPPPATHINC__(\d+)', + 'postrex': '__KEEPCPPPATHINC__([A-J]+)', 'postfunc': replace_cpppathinc } diff --git a/http/filters/dtsi.py b/http/filters/dtsi.py index c3c4f70a..8168fdf3 100644 --- a/http/filters/dtsi.py +++ b/http/filters/dtsi.py @@ -5,10 +5,10 @@ def keep_dtsi(m): dtsi.append(m.group(3)) - return m.group(1) + '/include/' + m.group(2) + '"__KEEPDTSI__' + str(len(dtsi)) + '"' + return m.group(1) + '/include/' + m.group(2) + '"__KEEPDTSI__' + encode_number(len(dtsi)) + '"' def replace_dtsi(m): - w = dtsi[int(m.group(1)) - 1] + w = dtsi[decode_number(m.group(1)) - 1] return ''+w+'' dtsi_filters = { @@ -16,7 +16,7 @@ def replace_dtsi(m): 'match': {'dts', 'dtsi'}, 'prerex': '^(\s*)/include/(\s*)\"(.*?)\"', 'prefunc': keep_dtsi, - 'postrex': '__KEEPDTSI__(\d+)', + 'postrex': '__KEEPDTSI__([A-J]+)', 'postfunc': replace_dtsi } diff --git a/http/filters/ident.py b/http/filters/ident.py index baa6a538..7dd2cfc2 100644 --- a/http/filters/ident.py +++ b/http/filters/ident.py @@ -4,17 +4,17 @@ def keep_idents(m): idents.append(m.group(1)) - return '__KEEPIDENTS__' + str(len(idents)) + return '__KEEPIDENTS__' + encode_number(len(idents)) def replace_idents(m): - i = idents[int(m.group(1)) - 1] + i = idents[decode_number(m.group(1)) - 1] return ''+i+'' ident_filters = { 'case': 'any', 'prerex': '\033\[31m(.*?)\033\[0m', 'prefunc': keep_idents, - 'postrex': '__KEEPIDENTS__(\d+)', + 'postrex': '__KEEPIDENTS__([A-J]+)', 'postfunc': replace_idents } diff --git a/http/filters/kconfig.py b/http/filters/kconfig.py index 540c3be5..d993fce4 100644 --- a/http/filters/kconfig.py +++ b/http/filters/kconfig.py @@ -4,10 +4,10 @@ def keep_kconfig(m): kconfig.append(m.group(4)) - return m.group(1) + m.group(2) + m.group(3) + '"__KEEPKCONFIG__' + str(len(kconfig)) + '"' + return m.group(1) + m.group(2) + m.group(3) + '"__KEEPKCONFIG__' + encode_number(len(kconfig)) + '"' def replace_kconfig(m): - w = kconfig[int(m.group(1)) - 1] + w = kconfig[decode_number(m.group(1)) - 1] return ''+w+'' kconfig_filters = { @@ -15,7 +15,7 @@ def replace_kconfig(m): 'match': {'Kconfig'}, 'prerex': '^(\s*)(source)(\s*)\"(.*?)\"', 'prefunc': keep_kconfig, - 'postrex': '__KEEPKCONFIG__(\d+)', + 'postrex': '__KEEPKCONFIG__([A-J]+)', 'postfunc': replace_kconfig } diff --git a/http/filters/makefiledir.py b/http/filters/makefiledir.py index 34322ad7..7b0feb66 100644 --- a/http/filters/makefiledir.py +++ b/http/filters/makefiledir.py @@ -5,10 +5,10 @@ def keep_makefiledir(m): makefiledir.append(m.group(1)) - return '__KEEPMAKEFILEDIR__' + str(len(makefiledir)) + '/' + m.group(2) + return '__KEEPMAKEFILEDIR__' + encode_number(len(makefiledir)) + '/' + m.group(2) def replace_makefiledir(m): - w = makefiledir[int(m.group(1)) - 1] + w = makefiledir[decode_number(m.group(1)) - 1] dir_name = os.path.dirname(path) if dir_name != '/': @@ -21,7 +21,7 @@ def replace_makefiledir(m): 'match': {'Makefile'}, 'prerex': '(?<=\s)([-\w/]+)/(\s+|$)', 'prefunc': keep_makefiledir, - 'postrex': '__KEEPMAKEFILEDIR__(\d+)/', + 'postrex': '__KEEPMAKEFILEDIR__([A-J]+)/', 'postfunc': replace_makefiledir } diff --git a/http/filters/makefiledtb.py b/http/filters/makefiledtb.py index 0971c88f..49db4a3d 100644 --- a/http/filters/makefiledtb.py +++ b/http/filters/makefiledtb.py @@ -5,10 +5,10 @@ def keep_makefiledtb(m): makefiledtb.append(m.group(1)) - return '__KEEPMAKEFILEDTB__' + str(len(makefiledtb)) + '.dtb' + return '__KEEPMAKEFILEDTB__' + encode_number(len(makefiledtb)) + '.dtb' def replace_makefiledtb(m): - w = makefiledtb[int(m.group(1)) - 1] + w = makefiledtb[decode_number(m.group(1)) - 1] dir_name = os.path.dirname(path) @@ -22,7 +22,7 @@ def replace_makefiledtb(m): 'match': {'Makefile'}, 'prerex': '(?<=\s)([-\w/]+)\.dtb', 'prefunc': keep_makefiledtb, - 'postrex': '__KEEPMAKEFILEDTB__(\d+)\.dtb', + 'postrex': '__KEEPMAKEFILEDTB__([A-J]+)\.dtb', 'postfunc': replace_makefiledtb } diff --git a/http/filters/makefilefile.py b/http/filters/makefilefile.py index 0aedd56f..b47d65b7 100644 --- a/http/filters/makefilefile.py +++ b/http/filters/makefilefile.py @@ -10,12 +10,12 @@ def keep_makefilefile(m): if query('exist', tag, dir_name + m.group(1)): makefilefile.append(m.group(1)) - return '__KEEPMAKEFILEFILE__' + str(len(makefilefile)) + m.group(2) + return '__KEEPMAKEFILEFILE__' + encode_number(len(makefilefile)) + m.group(2) else: return m.group(0) def replace_makefilefile(m): - w = makefilefile[int(m.group(1)) - 1] + w = makefilefile[decode_number(m.group(1)) - 1] dir_name = os.path.dirname(path) if dir_name != '/': @@ -28,7 +28,7 @@ def replace_makefilefile(m): 'match': {'Makefile'}, 'prerex': '(?<=\s)(?!/)([-\w/]+/[-\w\.]+)(\s+|$)', 'prefunc': keep_makefilefile, - 'postrex': '__KEEPMAKEFILEFILE__(\d+)', + 'postrex': '__KEEPMAKEFILEFILE__([A-J]+)', 'postfunc': replace_makefilefile } diff --git a/http/filters/makefileo.py b/http/filters/makefileo.py index bf557cd0..24cc39d8 100644 --- a/http/filters/makefileo.py +++ b/http/filters/makefileo.py @@ -5,10 +5,10 @@ def keep_makefileo(m): makefileo.append(m.group(1)) - return '__KEEPMAKEFILEO__' + str(len(makefileo)) + '.o' + return '__KEEPMAKEFILEO__' + encode_number(len(makefileo)) + '.o' def replace_makefileo(m): - w = makefileo[int(m.group(1)) - 1] + w = makefileo[decode_number(m.group(1)) - 1] dir_name = os.path.dirname(path) @@ -22,7 +22,7 @@ def replace_makefileo(m): 'match': {'Makefile'}, 'prerex': '(?<=\s)([-\w/]+)\.o(?!\w)', 'prefunc': keep_makefileo, - 'postrex': '__KEEPMAKEFILEO__(\d+)\.o', + 'postrex': '__KEEPMAKEFILEO__([A-J]+)\.o', 'postfunc': replace_makefileo } diff --git a/http/filters/makefilesubdir.py b/http/filters/makefilesubdir.py index 56d5abe8..dfdb4931 100644 --- a/http/filters/makefilesubdir.py +++ b/http/filters/makefilesubdir.py @@ -5,10 +5,10 @@ def keep_makefilesubdir(m): makefilesubdir.append(m.group(5)) - return m.group(1)+m.group(2)+m.group(3)+m.group(4)+'__KEEPMAKESUBDIR__' + str(len(makefilesubdir)) + m.group(6) + return m.group(1)+m.group(2)+m.group(3)+m.group(4)+'__KEEPMAKESUBDIR__' + encode_number(len(makefilesubdir)) + m.group(6) def replace_makefilesubdir(m): - w = makefilesubdir[int(m.group(1)) - 1] + w = makefilesubdir[decode_number(m.group(1)) - 1] dir_name = os.path.dirname(path) @@ -22,7 +22,7 @@ def replace_makefilesubdir(m): 'match': {'Makefile'}, 'prerex': '(subdir-y)(\s+)(\+=|:=)(\s+)([-\w]+)(\s*|$)', 'prefunc': keep_makefilesubdir, - 'postrex': '__KEEPMAKESUBDIR__(\d+)', + 'postrex': '__KEEPMAKESUBDIR__([A-J]+)', 'postfunc': replace_makefilesubdir } From fb23a3b9601f49bdcf3f75ee81d00821f55aa2ff Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 14 May 2020 10:29:26 +0200 Subject: [PATCH 062/529] filters/cpppathinc.py : Simplify code m.group(0) returns the whole regex match so we don't need to rebuild the matched string using other groups Signed-off-by: Maxime Chretien --- http/filters/cpppathinc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/filters/cpppathinc.py b/http/filters/cpppathinc.py index fb872128..a7f169e8 100644 --- a/http/filters/cpppathinc.py +++ b/http/filters/cpppathinc.py @@ -14,7 +14,7 @@ def keep_cpppathinc(m): if re.match('^asm/.*', inc): # Keep the original string in case the path contains "asm/" # Because there are then multiple include possibilites, one per architecture - return m1 + '#include' + m2 + '<' + inc + '>' + return m.group(0) else: cpppathinc.append(inc) return m1 + '#include' + m2 + '<__KEEPCPPPATHINC__' + encode_number(len(cpppathinc)) + '>' From 40fcdd5601b3a341939df8b9a71238221a4eba9c Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 14 May 2020 10:37:52 +0200 Subject: [PATCH 063/529] lib.py : Add 'return' in the blacklist This shouldn't be seen as an identifier because it is used in nearly every files Signed-off-by: Maxime Chretien --- lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib.py b/lib.py index a9a521af..ee98d2f4 100755 --- a/lib.py +++ b/lib.py @@ -154,6 +154,7 @@ def unescape(bstr): b'extern', b'driver', b'ptr', + b'return', ) def isIdent(bstr): From 07807db792d870e62cf71e22f028d9d68d9beaa0 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 09:42:07 +0200 Subject: [PATCH 064/529] filters : Add filter for symlinks This create links for file like this one : https://elixir.bootlin.com/linux/latest/source/scripts/dtc/include-prefixes/arm Signed-off-by: Maxime Chretien --- http/filters/linux.py | 1 + http/filters/symlinks.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 http/filters/symlinks.py diff --git a/http/filters/linux.py b/http/filters/linux.py index 3aa6ccfa..83844311 100644 --- a/http/filters/linux.py +++ b/http/filters/linux.py @@ -7,6 +7,7 @@ exec(open('makefiledir.py').read()) exec(open('makefilefile.py').read()) exec(open('makefilesubdir.py').read()) +exec(open('symlinks.py').read()) exec(open('cpppathinc.py').read()) # include/uapi contains includes to user headers under #ifndef __KERNEL__ diff --git a/http/filters/symlinks.py b/http/filters/symlinks.py new file mode 100644 index 00000000..0c7ea4c3 --- /dev/null +++ b/http/filters/symlinks.py @@ -0,0 +1,32 @@ +# Filter for symbolic links + +symlinks = [] + +def keep_symlinks(m): + dir_name = os.path.dirname(path) + rel_path = m.group(1) + + if dir_name != '/': + dir_name += '/' + + full_path = os.path.abspath(dir_name + rel_path) + + if query('exist', tag, full_path): + symlinks.append((full_path, rel_path)) + return '__KEEPSYMLINKS__' + encode_number(len(symlinks)) + m.group(2) + else: + return m.group(0) + +def replace_symlinks(m): + w, n = symlinks[decode_number(m.group(1)) - 1] + return ''+n+'' + +symlinks_filters = { + 'case': 'any', + 'prerex': '((?:\.\./)+[-\w/]+/[-\w\.]+)(\s+|$)', + 'prefunc': keep_symlinks, + 'postrex': '__KEEPSYMLINKS__([A-J]+)', + 'postfunc': replace_symlinks + } + +filters.append(symlinks_filters) From 04636596af4ae8f520e2ffe0a6d8e17eba62e5c2 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 11:19:24 +0200 Subject: [PATCH 065/529] filters: Add makefile filter for $(srctree) links A lot of files are listed using $(srctree) so this filter create links for those files Signed-off-by: Maxime Chretien --- http/filters/linux.py | 1 + http/filters/makefilesrctree.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 http/filters/makefilesrctree.py diff --git a/http/filters/linux.py b/http/filters/linux.py index 83844311..8fa5591d 100644 --- a/http/filters/linux.py +++ b/http/filters/linux.py @@ -7,6 +7,7 @@ exec(open('makefiledir.py').read()) exec(open('makefilefile.py').read()) exec(open('makefilesubdir.py').read()) +exec(open('makefilesrctree.py').read()) exec(open('symlinks.py').read()) exec(open('cpppathinc.py').read()) diff --git a/http/filters/makefilesrctree.py b/http/filters/makefilesrctree.py new file mode 100644 index 00000000..fdb6adf8 --- /dev/null +++ b/http/filters/makefilesrctree.py @@ -0,0 +1,25 @@ +# Filters for files listed in Makefiles using $(srctree) + +makefilesrctree = [] + +def keep_makefilesrctree(m): + if query('exist', tag, '/' + m.group(1)): + makefilesrctree.append(m.group(1)) + return '__KEEPMAKEFILESRCTREE__' + encode_number(len(makefilesrctree)) + m.group(2) + else: + return m.group(0) + +def replace_makefilesrctree(m): + w = makefilesrctree[decode_number(m.group(1)) - 1] + return ''+'$(srctree)/'+w+'' + +makefilesrctree_filters = { + 'case': 'filename', + 'match': {'Makefile'}, + 'prerex': '(?:(?<=\s)|(?<=-I))(?!/)\$\(srctree\)/((?:[-\w/]+/)?[-\w\.]+)(\s+|\)|$)', + 'prefunc': keep_makefilesrctree, + 'postrex': '__KEEPMAKEFILESRCTREE__([A-J]+)', + 'postfunc': replace_makefilesrctree + } + +filters.append(makefilesrctree_filters) From 2021d8209baf4c292084243056dcf1003899bc05 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 11:51:34 +0200 Subject: [PATCH 066/529] filters/makefilefile.py: Improve regex Improve regex to take in account possible -I at the beginning and ) at the end Signed-off-by: Maxime Chretien --- http/filters/makefilefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/filters/makefilefile.py b/http/filters/makefilefile.py index b47d65b7..14469b4a 100644 --- a/http/filters/makefilefile.py +++ b/http/filters/makefilefile.py @@ -26,7 +26,7 @@ def replace_makefilefile(m): makefilefile_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '(?<=\s)(?!/)([-\w/]+/[-\w\.]+)(\s+|$)', + 'prerex': '(?:(?<=\s)|(?<=-I))(?!/)([-\w/]+/[-\w\.]+)(\s+|\)|$)', 'prefunc': keep_makefilefile, 'postrex': '__KEEPMAKEFILEFILE__([A-J]+)', 'postfunc': replace_makefilefile From 65bc7e63a5cb1594ed33b05256979f386eb90316 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 11:54:25 +0200 Subject: [PATCH 067/529] filters/makefile(file|srctree).py: Possible = at the beginning Improve regex to take in account possible = at the beginning Signed-off-by: Maxime Chretien --- http/filters/makefilefile.py | 2 +- http/filters/makefilesrctree.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/http/filters/makefilefile.py b/http/filters/makefilefile.py index 14469b4a..21e3dc58 100644 --- a/http/filters/makefilefile.py +++ b/http/filters/makefilefile.py @@ -26,7 +26,7 @@ def replace_makefilefile(m): makefilefile_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '(?:(?<=\s)|(?<=-I))(?!/)([-\w/]+/[-\w\.]+)(\s+|\)|$)', + 'prerex': '(?:(?<=\s|=)|(?<=-I))(?!/)([-\w/]+/[-\w\.]+)(\s+|\)|$)', 'prefunc': keep_makefilefile, 'postrex': '__KEEPMAKEFILEFILE__([A-J]+)', 'postfunc': replace_makefilefile diff --git a/http/filters/makefilesrctree.py b/http/filters/makefilesrctree.py index fdb6adf8..56d6e702 100644 --- a/http/filters/makefilesrctree.py +++ b/http/filters/makefilesrctree.py @@ -16,7 +16,7 @@ def replace_makefilesrctree(m): makefilesrctree_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '(?:(?<=\s)|(?<=-I))(?!/)\$\(srctree\)/((?:[-\w/]+/)?[-\w\.]+)(\s+|\)|$)', + 'prerex': '(?:(?<=\s|=)|(?<=-I))(?!/)\$\(srctree\)/((?:[-\w/]+/)?[-\w\.]+)(\s+|\)|$)', 'prefunc': keep_makefilesrctree, 'postrex': '__KEEPMAKEFILESRCTREE__([A-J]+)', 'postfunc': replace_makefilesrctree From 25c1f5cc5eed1b799bc8d389ff2a2f202790cc51 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 13:53:31 +0200 Subject: [PATCH 068/529] filters/makefiledir.py: Check if the file exists Check if the file exists before creating a link to dir/Makefile this prevent conflicts with sed syntax like sed -e s/sa110/arm/ Signed-off-by: Maxime Chretien --- http/filters/makefiledir.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/http/filters/makefiledir.py b/http/filters/makefiledir.py index 7b0feb66..527df3df 100644 --- a/http/filters/makefiledir.py +++ b/http/filters/makefiledir.py @@ -4,8 +4,16 @@ makefiledir = [] def keep_makefiledir(m): - makefiledir.append(m.group(1)) - return '__KEEPMAKEFILEDIR__' + encode_number(len(makefiledir)) + '/' + m.group(2) + dir_name = os.path.dirname(path) + + if dir_name != '/': + dir_name += '/' + + if query('exist', tag, dir_name + m.group(1) + '/Makefile'): + makefiledir.append(m.group(1)) + return '__KEEPMAKEFILEDIR__' + encode_number(len(makefiledir)) + '/' + m.group(2) + else: + return m.group(0) def replace_makefiledir(m): w = makefiledir[decode_number(m.group(1)) - 1] From b15bff4ebe3e56e10563700a46474e41de9647ea Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 14:00:30 +0200 Subject: [PATCH 069/529] filters: Enable new Makefiles filters for u-boot and barebox Signed-off-by: Maxime Chretien --- http/filters/barebox.py | 2 ++ http/filters/u-boot.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/http/filters/barebox.py b/http/filters/barebox.py index 6c2b9e99..4c39138d 100644 --- a/http/filters/barebox.py +++ b/http/filters/barebox.py @@ -7,3 +7,5 @@ exec(open('makefiledtb.py').read()) exec(open('makefiledir.py').read()) exec(open('makefilesubdir.py').read()) +exec(open('makefilefile.py').read()) +exec(open('makefilesrctree.py').read()) diff --git a/http/filters/u-boot.py b/http/filters/u-boot.py index fce18a67..784ee914 100644 --- a/http/filters/u-boot.py +++ b/http/filters/u-boot.py @@ -7,3 +7,5 @@ exec(open('makefiledtb.py').read()) exec(open('makefiledir.py').read()) exec(open('makefilesubdir.py').read()) +exec(open('makefilefile.py').read()) +exec(open('makefilesrctree.py').read()) From b6b38b27cd49f3ade3b194e05e0ad2c2bbb8a3a1 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 15 May 2020 14:40:09 +0200 Subject: [PATCH 070/529] filters/makefiledtb.py: Some files have + and . in their name For example : https://elixir.bootlin.com/linux/latest/source/arch/arm/boot/dts/Makefile#L297 https://elixir.bootlin.com/linux/latest/source/arch/arm/boot/dts/Makefile#L867 Signed-off-by: Maxime Chretien --- http/filters/makefiledtb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/filters/makefiledtb.py b/http/filters/makefiledtb.py index 49db4a3d..b282c106 100644 --- a/http/filters/makefiledtb.py +++ b/http/filters/makefiledtb.py @@ -20,7 +20,7 @@ def replace_makefiledtb(m): makefiledtb_filters = { 'case': 'filename', 'match': {'Makefile'}, - 'prerex': '(?<=\s)([-\w/]+)\.dtb', + 'prerex': '(?<=\s)([-\w/+\.]+)\.dtb', 'prefunc': keep_makefiledtb, 'postrex': '__KEEPMAKEFILEDTB__([A-J]+)\.dtb', 'postfunc': replace_makefiledtb From 9f29697105e16ff98ff7c7d7130bce1a17299a2d Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Mon, 18 May 2020 10:21:45 +0200 Subject: [PATCH 071/529] symlinks: Handle symlinks in the tree view git ls-tree return file permissions and symlinks always have permission 120000 So we can use that to identify them Signed-off-by: Maxime Chretien --- http/web.py | 19 ++++++++++++++++--- script.sh | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/http/web.py b/http/web.py index 71db31ed..48e7294f 100755 --- a/http/web.py +++ b/http/web.py @@ -186,7 +186,7 @@ def print(arg, end='\n'): else path_split[-1]+' - '+'/'.join(path_split)+' - ') \ +title_suffix - lines = ['null - -'] + lines = ['null - - -'] type = query('type', tag, path) if len(type) > 0: @@ -200,12 +200,12 @@ def print(arg, end='\n'): if type == 'tree': if path != '': - lines[0] = 'back - -' + lines[0] = 'back - - -' print('
') print('\n') for l in lines: - type, name, size = l.split(' ') + type, name, size, perm = l.split(' ') if type == 'null': continue @@ -216,6 +216,19 @@ def print(arg, end='\n'): elif type == 'blob': size = size+' bytes' path2 = path+'/'+name + + if perm == '120000': + # 120000 permission means it's a symlink + # So we need to handle that correctly + dir_name = os.path.dirname(path) + rel_path = query('file', tag, path2) + + if dir_name != '/': + dir_name += '/' + + path2 = os.path.abspath(dir_name + rel_path) + + name = name + ' -> ' + path2 elif type == 'back': size = '' path2 = os.path.dirname(path[:-1]) diff --git a/script.sh b/script.sh index 80f80048..bf08e45d 100755 --- a/script.sh +++ b/script.sh @@ -87,7 +87,7 @@ get_dir() { v=`echo $opt1 | version_rev` git ls-tree -l "$v:`denormalize $opt2`" 2>/dev/null | - awk '{print $2" "$5" "$4}' | + awk '{print $2" "$5" "$4" "$1}' | grep -v ' \.' | sort -t ' ' -k 1,1r -k 2,2 } From adb6d1244d95f1e075382367456d1d53353cd6cd Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Mon, 18 May 2020 10:55:28 +0200 Subject: [PATCH 072/529] Revert "filters : Add filter for symlinks" This reverts commit 07807db792d870e62cf71e22f028d9d68d9beaa0. Signed-off-by: Maxime Chretien --- http/filters/linux.py | 1 - http/filters/symlinks.py | 32 -------------------------------- 2 files changed, 33 deletions(-) delete mode 100644 http/filters/symlinks.py diff --git a/http/filters/linux.py b/http/filters/linux.py index 8fa5591d..12d0866e 100644 --- a/http/filters/linux.py +++ b/http/filters/linux.py @@ -8,7 +8,6 @@ exec(open('makefilefile.py').read()) exec(open('makefilesubdir.py').read()) exec(open('makefilesrctree.py').read()) -exec(open('symlinks.py').read()) exec(open('cpppathinc.py').read()) # include/uapi contains includes to user headers under #ifndef __KERNEL__ diff --git a/http/filters/symlinks.py b/http/filters/symlinks.py deleted file mode 100644 index 0c7ea4c3..00000000 --- a/http/filters/symlinks.py +++ /dev/null @@ -1,32 +0,0 @@ -# Filter for symbolic links - -symlinks = [] - -def keep_symlinks(m): - dir_name = os.path.dirname(path) - rel_path = m.group(1) - - if dir_name != '/': - dir_name += '/' - - full_path = os.path.abspath(dir_name + rel_path) - - if query('exist', tag, full_path): - symlinks.append((full_path, rel_path)) - return '__KEEPSYMLINKS__' + encode_number(len(symlinks)) + m.group(2) - else: - return m.group(0) - -def replace_symlinks(m): - w, n = symlinks[decode_number(m.group(1)) - 1] - return ''+n+'' - -symlinks_filters = { - 'case': 'any', - 'prerex': '((?:\.\./)+[-\w/]+/[-\w\.]+)(\s+|$)', - 'prefunc': keep_symlinks, - 'postrex': '__KEEPSYMLINKS__([A-J]+)', - 'postfunc': replace_symlinks - } - -filters.append(symlinks_filters) From ded9a1112e70e2b8ea41d867e630a2d66426a7bb Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Tue, 19 May 2020 17:38:26 +0200 Subject: [PATCH 073/529] query.py: get latest indexed version instead of latest git version This prevent bad queries when the git repo is more up to date than the database. Signed-off-by: Maxime Chretien --- query.py | 14 ++++++++++---- script.sh | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/query.py b/query.py index 6a744a3f..3665223c 100755 --- a/query.py +++ b/query.py @@ -84,11 +84,17 @@ def query(cmd, *args): elif cmd == 'latest': # Returns the tag considered as the latest one - # TODO: this latest tag may have just been retrieved - # in the git repository and may not have been indexed yet - # This could results in failed queries + previous = None + tag = '' + index = 0 - return decode(script('get-latest')).rstrip('\n') + # If we get the same tag twice, we are at the oldest one + while not db.vers.exists(tag) and previous != tag: + previous = tag + tag = decode(script('get-latest', str(index))).rstrip('\n') + index += 1 + + return tag elif cmd == 'type': diff --git a/script.sh b/script.sh index bf08e45d..bf6275f2 100755 --- a/script.sh +++ b/script.sh @@ -63,7 +63,7 @@ list_tags_h() get_latest() { - git tag | version_dir | grep -v '\-rc' | sort -V | tail -n 1 + git tag | version_dir | grep -v '\-rc' | sort -V | tail -n $(($opt1 + 1)) | head -1 } get_type() From 92aaafcd5d55d0bb8b7a8d59ed3235052f510d91 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Wed, 20 May 2020 12:14:34 +0200 Subject: [PATCH 074/529] README: Update informations about pygments installation The file has been updated to enable Kconfig highlighting for file with names like Kconfig.x86, Kconfig.debug, Kconfig-nommu, ... Signed-off-by: Maxime Chretien --- README.adoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.adoc b/README.adoc index afda117e..f1f3db39 100644 --- a/README.adoc +++ b/README.adoc @@ -90,20 +90,20 @@ to know what packages Elixir needs in your favorite distribution. == Special dependencies -=== Device Tree Source syntax highlighting +=== Device Tree and Kconfig Source syntax highlighting The service on https://elixir.bootlin.com relies on a modified version of https://pygments.org/[Pygments], -to enable syntax highlighting on Device Tree Source (DTS) files. +to enable syntax highlighting on Device Tree Source (DTS) files and on all Kconfig files. The changes have been sent upstream and should appear in future distributions. In the meantime, you can either rebuild this package from its source on -https://github.com/MaximeChretien/pygments/tree/devicetree-lexer[GitHub], or download this -https://bootlin.com/pub/elixir/Pygments-2.6.1.devicetree-py3-none-any.whl[binary module] +https://github.com/MaximeChretien/pygments/tree/dev[GitHub], or download this +https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl[binary module] and install it as follows: ---- -sudo pip3-install Pygments-2.6.1.devicetree-py3-none-any.whl +sudo pip3-install Pygments-2.6.1.elixir-py3-none-any.whl ---- == Download Elixir Project From 99d5ef7c842c98fa8e4980b13c2c126b4257b61b Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 22 May 2020 10:28:08 +0200 Subject: [PATCH 075/529] Fix minor typo in documentation Signed-off-by: Michael Opdenacker --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index f1f3db39..63ff40ac 100644 --- a/README.adoc +++ b/README.adoc @@ -103,7 +103,7 @@ https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl[binary mod and install it as follows: ---- -sudo pip3-install Pygments-2.6.1.elixir-py3-none-any.whl +sudo pip3 install Pygments-2.6.1.elixir-py3-none-any.whl ---- == Download Elixir Project From bdcb38662697ff2a4d49f8ef6148f8a106f9ecc5 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 22 May 2020 09:40:25 +0200 Subject: [PATCH 076/529] update.py: Use 4 threads to speed up indexation This allows to index multiple things at the same time so it speeds up database indexation. Signed-off-by: Maxime Chretien --- update.py | 402 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 275 insertions(+), 127 deletions(-) diff --git a/update.py b/update.py index c65d2c5a..c1866d65 100755 --- a/update.py +++ b/update.py @@ -27,158 +27,306 @@ import data import os from data import PathList +from threading import Thread, Lock, Event, Condition verbose = False db = data.DB(lib.getDataDir(), readonly=False) -# Store new blobs hashed and file names (without path) for new tag -def updateBlobIDs(tag): +hash_file_lock = Lock() #Lock for db.hash and db.file +defs_lock = Lock() #Lock for db.defs +tag_ready = Condition() #Waiting for new tags - if db.vars.exists('numBlobs'): - idx = db.vars.get('numBlobs') - else: - idx = 0 +new_idxes = [] # (new idxes, Event idxes ready, Event defs ready) - # Get blob hashes and associated file names (without path) - blobs = scriptLines('list-blobs', '-f', tag) +tags_done = False #True if all tags have been added to new_idxes - newIdxes = [] - for blob in blobs: - hash, filename = blob.split(b' ',maxsplit=1) - if not db.blob.exists(hash): - db.blob.put(hash, idx) - db.hash.put(idx, hash) - db.file.put(idx, filename) - newIdxes.append(idx) - if verbose: - print(f"New blob #{idx} {hash}:{filename}") - idx += 1 - db.vars.put('numBlobs', idx) - return newIdxes - -def updateVersions(tag): - - # Get blob hashes and associated file paths - blobs = scriptLines('list-blobs', '-p', tag) - buf = [] - - for blob in blobs: - hash, path = blob.split(b' ', maxsplit=1) - idx = db.blob.get(hash) - buf.append((idx, path)) - - buf = sorted(buf) - obj = PathList() - for idx, path in buf: - obj.append(idx, path) - if verbose: - print(f"Tag {tag}: adding #{idx} {path}") - db.vers.put(tag, obj, sync=True) - -def updateDefinitions(idxes): - for idx in idxes: - if (idx % 1000 == 0): progress('defs: ' + str(idx)) - hash = db.hash.get(idx) - filename = db.file.get(idx) - - if not lib.hasSupportedExt(filename): continue - - lines = scriptLines('parse-defs', hash, filename) - for l in lines: - ident, type, line = l.split(b' ') - type = type.decode() - line = int(line.decode()) - - if db.defs.exists(ident): - obj = db.defs.get(ident) - else: - obj = data.DefList() - - obj.append(idx, type, line) + +class UpdateIdVersion(Thread): + def __init__(self, tag_buf): + Thread.__init__(self, name="UpdateIdVersionElixir") + self.tag_buf = tag_buf + + def run(self): + global new_idxes, tags_done, tag_ready + self.index = 0 + + for tag in self.tag_buf: + + new_idxes.append((self.update_blob_ids(tag), Event(), Event())) + + progress(tag.decode() + ': ' + str(len(new_idxes[self.index][0])) + + ' new blobs', self.index+1) + + self.update_versions(tag) + + new_idxes[self.index][1].set() #Tell that the tag is ready + + self.index += 1 + + #Wake up waiting threads + with tag_ready: + tag_ready.notify_all() + + tags_done = True + + def update_blob_ids(self, tag): + + global hash_file_lock + + if db.vars.exists('numBlobs'): + idx = db.vars.get('numBlobs') + else: + idx = 0 + + # Get blob hashes and associated file names (without path) + blobs = scriptLines('list-blobs', '-f', tag) + + new_idxes = [] + for blob in blobs: + hash, filename = blob.split(b' ',maxsplit=1) + if not db.blob.exists(hash): + db.blob.put(hash, idx) + + with hash_file_lock: + db.hash.put(idx, hash) + db.file.put(idx, filename) + + new_idxes.append(idx) + if verbose: + print(f"New blob #{idx} {hash}:{filename}") + idx += 1 + db.vars.put('numBlobs', idx) + return new_idxes + + def update_versions(self, tag): + + # Get blob hashes and associated file paths + blobs = scriptLines('list-blobs', '-p', tag) + buf = [] + + for blob in blobs: + hash, path = blob.split(b' ', maxsplit=1) + idx = db.blob.get(hash) + buf.append((idx, path)) + + buf = sorted(buf) + obj = PathList() + for idx, path in buf: + obj.append(idx, path) if verbose: - print(f"def {type} {ident} in #{idx} @ {line}") - db.defs.put(ident, obj) - -def updateReferences(idxes): - for idx in idxes: - if (idx % 1000 == 0): progress('refs: ' + str(idx)) - hash = db.hash.get(idx) - filename = db.file.get(idx) - - if not lib.hasSupportedExt(filename): continue - - tokens = scriptLines('tokenize-file', '-b', hash) - even = True - lineNum = 1 - idents = {} - for tok in tokens: - even = not even - if even: - if db.defs.exists(tok) and lib.isIdent(tok): - if tok in idents: - idents[tok] += ',' + str(lineNum) + print(f"Tag {tag}: adding #{idx} {path}") + db.vers.put(tag, obj, sync=True) + + + +class UpdateDefs(Thread): + def __init__(self): + Thread.__init__(self, name="UpdateDefsElixir") + + def run(self): + global new_idxes, tags_done, tag_ready + + self.index = 0 + + while(not (tags_done and self.index == len(new_idxes))): + if(self.index == len(new_idxes)): + #Wait for new tags + with tag_ready: + tag_ready.wait() + continue + + new_idxes[self.index][1].wait() #Make sure the tag is ready + + self.update_definitions(new_idxes[self.index][0]) + + new_idxes[self.index][2].set() #Tell that UpdateDefs processed the tag + + self.index += 1 + + + def update_definitions(self, idxes): + global hash_file_lock, defs_lock + + for idx in idxes: + if (idx % 1000 == 0): progress('defs: ' + str(idx), self.index+1) + + with hash_file_lock: + hash = db.hash.get(idx) + filename = db.file.get(idx) + + if not lib.hasSupportedExt(filename): continue + + lines = scriptLines('parse-defs', hash, filename) + for l in lines: + ident, type, line = l.split(b' ') + type = type.decode() + line = int(line.decode()) + + with defs_lock: + if db.defs.exists(ident): + obj = db.defs.get(ident) else: - idents[tok] = str(lineNum) - else: - lineNum += tok.count(b'\1') + obj = data.DefList() - for ident, lines in idents.items(): - if db.refs.exists(ident): - obj = db.refs.get(ident) - else: - obj = data.RefList() + obj.append(idx, type, line) + if verbose: + print(f"def {type} {ident} in #{idx} @ {line}") + with defs_lock: + db.defs.put(ident, obj) - obj.append(idx, lines) - if verbose: - print(f"ref: {ident} in #{idx} @ {lines}") - db.refs.put(ident, obj) -def updateDocComments(idxes): - for idx in idxes: - if (idx % 1000 == 0): progress('docs: ' + str(idx)) - hash = db.hash.get(idx) - filename = db.file.get(idx) +class UpdateRefs(Thread): + def __init__(self): + Thread.__init__(self, name="UpdateRefsElixir") - if not lib.hasSupportedExt(filename): continue + def run(self): + global new_idxes, tags_done - lines = scriptLines('parse-docs', hash, filename) - for l in lines: - ident, line = l.split(b' ') - line = int(line.decode()) + self.index = 0 - if db.docs.exists(ident): - obj = db.docs.get(ident) - else: - obj = data.RefList() + while(not (tags_done and self.index == len(new_idxes))): + if(self.index == len(new_idxes)): + #Wait for new tags + with tag_ready: + tag_ready.wait() + continue - obj.append(idx, str(line)) - if verbose: - print(f"doc: {ident} in #{idx} @ {line}") - db.docs.put(ident, obj) + new_idxes[self.index][1].wait() #Make sure the tag is ready + new_idxes[self.index][2].wait() #Make sure UpdateDefs processed the tag + + self.update_references(new_idxes[self.index][0]) + + self.index += 1 + + def update_references(self, idxes): + global hash_file_lock, defs_lock + + for idx in idxes: + if (idx % 1000 == 0): progress('refs: ' + str(idx), self.index+1) + + with hash_file_lock: + hash = db.hash.get(idx) + filename = db.file.get(idx) + + if not lib.hasSupportedExt(filename): continue + + tokens = scriptLines('tokenize-file', '-b', hash) + even = True + line_num = 1 + idents = {} + for tok in tokens: + even = not even + if even: + + with defs_lock: + if db.defs.exists(tok) and lib.isIdent(tok): + if tok in idents: + idents[tok] += ',' + str(line_num) + else: + idents[tok] = str(line_num) + + else: + line_num += tok.count(b'\1') + + for ident, lines in idents.items(): + if db.refs.exists(ident): + obj = db.refs.get(ident) + else: + obj = data.RefList() + + obj.append(idx, lines) + if verbose: + print(f"ref: {ident} in #{idx} @ {lines}") + db.refs.put(ident, obj) + + +class UpdateDocs(Thread): + def __init__(self): + Thread.__init__(self, name="UpdateDocsElixir") + + def run(self): + global new_idxes, tags_done + + self.index = 0 + + while(not (tags_done and self.index == len(new_idxes))): + if(self.index == len(new_idxes)): + #Wait for new tags + with tag_ready: + tag_ready.wait() + continue + + new_idxes[self.index][1].wait() #Make sure the tag is ready + + self.update_doc_comments(new_idxes[self.index][0]) + + self.index += 1 + + def update_doc_comments(self, idxes): + global hash_file_lock + + for idx in idxes: + if (idx % 1000 == 0): progress('docs: ' + str(idx), self.index+1) + + with hash_file_lock: + hash = db.hash.get(idx) + filename = db.file.get(idx) + + if not lib.hasSupportedExt(filename): continue + + lines = scriptLines('parse-docs', hash, filename) + for l in lines: + ident, line = l.split(b' ') + line = int(line.decode()) + + if db.docs.exists(ident): + obj = db.docs.get(ident) + else: + obj = data.RefList() + + obj.append(idx, str(line)) + if verbose: + print(f"doc: {ident} in #{idx} @ {line}") + db.docs.put(ident, obj) + + +def progress(msg, current): + print('{} - {} ({:.0%})'.format(project, msg, current/num_tags)) -def progress(msg): - print('{} - {} ({:.0%})'.format(project, msg, tagCount/numTags)) # Main -tagBuf = [] +tag_buf = [] for tag in scriptLines('list-tags'): if not db.vers.exists(tag): - tagBuf.append(tag) + tag_buf.append(tag) -numTags = len(tagBuf) -tagCount = 0 +num_tags = len(tag_buf) project = lib.currentProject() -print(project + ' - found ' + str(len(tagBuf)) + ' new tags') +print(project + ' - found ' + str(len(tag_buf)) + ' new tags') + +id_version_thread = UpdateIdVersion(tag_buf) +defs_thread = UpdateDefs() +refs_thread = UpdateRefs() +docs_thread = UpdateDocs() + +#Start to process tags +id_version_thread.start() + +#Wait until the first tag is ready +with tag_ready: + tag_ready.wait() + +#Start remaining threads +defs_thread.start() +refs_thread.start() +docs_thread.start() -for tag in tagBuf: - tagCount +=1 - newIdxes = updateBlobIDs(tag) - progress(tag.decode() + ': ' + str(len(newIdxes)) + ' new blobs') - updateVersions(tag) - updateDefinitions(newIdxes) - updateReferences(newIdxes) - updateDocComments(newIdxes) +#Make sure all threads finished +id_version_thread.join() +defs_thread.join() +refs_thread.join() +docs_thread.join() From 72571fbfd91a4572d106ba5ec33f763d03b8d87e Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 22 May 2020 09:44:43 +0200 Subject: [PATCH 077/529] database: Add support for Kconfig et Devicetree files This brings the idea of file families, each ident is identified by his family and can be referenced in compatible families. For exemple : - A Kconfig ident can be referenced in a C file but not in a Devicetree file. - A Devicetree ident is only referenced in Devicetree files. - A C ident is only referenced in C files. Kconfig idents are defined without the CONFIG_ at the beginning. We add it while indexing for an easier processing of other files. Signed-off-by: Maxime Chretien --- data.py | 47 +++++++++++++++++++++++++++++++---------------- lib.py | 28 +++++++++++++++++++++++++--- script.sh | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- update.py | 28 +++++++++++++++++++--------- 4 files changed, 122 insertions(+), 31 deletions(-) diff --git a/data.py b/data.py index 8f088bd3..f16c7842 100755 --- a/data.py +++ b/data.py @@ -29,6 +29,7 @@ ################################################################################## defTypeR = { + 'c': 'config', 'd': 'define', 'e': 'enum', 'E': 'enumerator', @@ -50,31 +51,43 @@ class DefList: '''Stores associations between a blob ID, a type (e.g., "function"), - and a line number.''' - def __init__(self, data=b''): - self.data = data + a line number and a file family. + Also stores in which families the ident exists for faster tests.''' + def __init__(self, data=b'#'): + self.data, self.families = data.split(b'#') def iter(self, dummy=False): for p in self.data.split(b','): - p = re.search(b'(\d*)(\w)(\d*)', p) - id, type, line = p.groups() + p = re.search(b'(\d*)(\w)(\d*)(\w)', p) + id, type, line, family = p.groups() id = int(id) type = defTypeR [type.decode()] line = int(line) - yield(id, type, line) + family = family.decode() + yield(id, type, line, family) if dummy: - yield(maxId, None, None) + yield(maxId, None, None, None) - def append(self, id, type, line): + def append(self, id, type, line, family): if type not in defTypeD: return - p = str(id) + defTypeD[type] + str(line) + p = str(id) + defTypeD[type] + str(line) + family if self.data != b'': p = ',' + p self.data += p.encode() def pack(self): - return self.data + return self.data + b'#' + self.families + + def add_family(self, family): + family = family.encode() + if not family in self.families.split(b','): + if self.families != b'': + family = b',' + family + self.families += family + + def get_families(self): + return self.families.decode().split(',') class PathList: '''Stores associations between a blob ID and a file path. @@ -100,7 +113,8 @@ def pack(self): return self.data class RefList: - '''Stores a mapping from blob ID to list of lines.''' + '''Stores a mapping from blob ID to list of lines + and the corresponding family.''' def __init__(self, data=b''): self.data = data @@ -110,16 +124,17 @@ def iter(self, dummy=False): while s.tell() < size: line = s.readline() line = line [:-1] - b,c = line.split(b':') + b,c,d = line.split(b':') b = int(b.decode()) c = c.decode() - yield(b, c) + d = d.decode() + yield(b, c, d) s.close() if dummy: - yield(maxId, None) + yield(maxId, None, None) - def append(self, id, lines): - p = str(id) + ':' + lines + '\n' + def append(self, id, lines, family): + p = str(id) + ':' + lines + ':' + family + '\n' self.data += p.encode() def pack(self): diff --git a/lib.py b/lib.py index ee98d2f4..0d924a9a 100755 --- a/lib.py +++ b/lib.py @@ -183,6 +183,28 @@ def getDataDir(): def currentProject(): return os.path.basename(os.path.dirname(getDataDir())) -def hasSupportedExt(filename): - ext = os.path.splitext(filename)[1] - return ext.lower() in ['.c', '.cc', '.cpp', '.c++', '.cxx', '.h', '.s'] +def getFileFamily(filename): + name, ext = os.path.splitext(filename) + + if ext.lower() in ['.c', '.cc', '.cpp', '.c++', '.cxx', '.h', '.s'] : + return 'C' # C file family and ASM + elif ext.lower() in ['.dts', '.dtsi'] : + return 'D' # Devicetree files + elif name.lower()[:7] in ['kconfig'] and not ext.lower() in ['.rst']: + # Some files are named like Kconfig-nommu so we only check the first 7 letters + # We also exclude documentation files that can be named kconfig + return 'K' # Kconfig files + else : + return None + +compatibility_list = { + 'C' : ['C', 'K'], + 'K' : ['K'], + 'D' : ['D'] +} + +# Check if families are compatible +# First argument can be a list of different families +# Second argument is the key for chossing the right array in the compatibility list +def compatibleFamily(file_family, requested_family): + return any(item in file_family for item in compatibility_list[requested_family]) diff --git a/script.sh b/script.sh index bf6275f2..1a363024 100755 --- a/script.sh +++ b/script.sh @@ -101,9 +101,15 @@ tokenize_file() ref="$v:`denormalize $opt2`" fi + if [ $opt3 = "D" ]; then #Don't cut around '-' in devicetrees + regex='s%((/\*.*?\*/|//.*?\001|[^'"'"']"(\\.|.)*?"|# *include *<.*?>|[^\w-])+)([\w-]+)?%\1\n\4\n%g' + else + regex='s%((/\*.*?\*/|//.*?\001|[^'"'"']"(\\.|.)*?"|# *include *<.*?>|\W)+)(\w+)?%\1\n\4\n%g' + fi + git cat-file blob $ref 2>/dev/null | tr '\n' '\1' | - perl -pe 's%((/\*.*?\*/|//.*?\001|[^'"'"']"(\\.|.)*?"|# *include *<.*?>|\W)+)(\w+)?%\1\n\4\n%g' | + perl -pe "$regex" | head -n -1 } @@ -136,12 +142,49 @@ untokenize() } parse_defs() +{ + case $opt3 in + "C") + parse_defs_C + ;; + "K") + parse_defs_K + ;; + "D") + parse_defs_D + ;; + esac +} + +parse_defs_C() +{ + tmp=`mktemp -d` + full_path=$tmp/$opt2 + git cat-file blob "$opt1" > "$full_path" + ctags -x --kinds-c=+p-m "$full_path" | + grep -avE "^operator |CONFIG_" | + awk '{print $1" "$2" "$3}' + rm "$full_path" + rmdir $tmp +} + +parse_defs_K() +{ + tmp=`mktemp -d` + full_path=$tmp/$opt2 + git cat-file blob "$opt1" > "$full_path" + ctags -x --language-force=kconfig "$full_path" | + awk '{print "CONFIG_"$1" "$2" "$3}' + rm "$full_path" + rmdir $tmp +} + +parse_defs_D() { tmp=`mktemp -d` full_path=$tmp/$opt2 git cat-file blob "$opt1" > "$full_path" - ctags -x --c-kinds=+p-m "$full_path" | - grep -av "^operator " | + ctags -x --language-force=dts "$full_path" | awk '{print $1" "$2" "$3}' rm "$full_path" rmdir $tmp @@ -171,6 +214,7 @@ test $# -gt 0 || set help cmd=$1 opt1=$2 opt2=$3 +opt3=$4 shift denormalize() diff --git a/update.py b/update.py index c1866d65..4102fe0e 100755 --- a/update.py +++ b/update.py @@ -156,9 +156,10 @@ def update_definitions(self, idxes): hash = db.hash.get(idx) filename = db.file.get(idx) - if not lib.hasSupportedExt(filename): continue + family = lib.getFileFamily(filename); + if family == None: continue - lines = scriptLines('parse-defs', hash, filename) + lines = scriptLines('parse-defs', hash, filename, family) for l in lines: ident, type, line = l.split(b' ') type = type.decode() @@ -170,7 +171,8 @@ def update_definitions(self, idxes): else: obj = data.DefList() - obj.append(idx, type, line) + obj.add_family(family) + obj.append(idx, type, line, family) if verbose: print(f"def {type} {ident} in #{idx} @ {line}") with defs_lock: @@ -210,16 +212,23 @@ def update_references(self, idxes): hash = db.hash.get(idx) filename = db.file.get(idx) - if not lib.hasSupportedExt(filename): continue + family = lib.getFileFamily(filename) + if family == None: continue - tokens = scriptLines('tokenize-file', '-b', hash) + prefix = b'' + # Kconfig values are saved as CONFIG_ + if family == 'K': + prefix = b'CONFIG_' + + tokens = scriptLines('tokenize-file', '-b', hash, family) even = True line_num = 1 idents = {} for tok in tokens: even = not even if even: - + tok = prefix + tok + with defs_lock: if db.defs.exists(tok) and lib.isIdent(tok): if tok in idents: @@ -236,7 +245,7 @@ def update_references(self, idxes): else: obj = data.RefList() - obj.append(idx, lines) + obj.append(idx, lines, family) if verbose: print(f"ref: {ident} in #{idx} @ {lines}") db.refs.put(ident, obj) @@ -274,7 +283,8 @@ def update_doc_comments(self, idxes): hash = db.hash.get(idx) filename = db.file.get(idx) - if not lib.hasSupportedExt(filename): continue + family = lib.getFileFamily(filename) + if family == None: continue lines = scriptLines('parse-docs', hash, filename) for l in lines: @@ -286,7 +296,7 @@ def update_doc_comments(self, idxes): else: obj = data.RefList() - obj.append(idx, str(line)) + obj.append(idx, str(line), family) if verbose: print(f"doc: {ident} in #{idx} @ {line}") db.docs.put(ident, obj) From 03f1d61e26d88b2418e2a36756d59cebca94bfc8 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 22 May 2020 10:10:42 +0200 Subject: [PATCH 078/529] query.py: Add support for file families in queries Also add a new query to get the family of a file. Signed-off-by: Maxime Chretien --- query.py | 51 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/query.py b/query.py index 3665223c..53eecdd4 100755 --- a/query.py +++ b/query.py @@ -146,14 +146,24 @@ def query(cmd, *args): version = args[0] path = args[1] - if lib.hasSupportedExt(path): + filename = os.path.basename(path) + family = lib.getFileFamily(filename) + + if family != None: buffer = BytesIO() - tokens = scriptLines('tokenize-file', version, path) + tokens = scriptLines('tokenize-file', version, path, family) even = True + + prefix = b'' + if family == 'K': + prefix = b'CONFIG_' + for tok in tokens: even = not even - if even and db.defs.exists(tok) and lib.isIdent(tok): - tok = b'\033[31m' + tok + b'\033[0m' + tok2 = prefix + tok + if (even and db.defs.exists(tok2) and lib.isIdent(tok2) + and lib.compatibleFamily(db.defs.get(tok2).get_families(), family)): + tok = b'\033[31m' + tok2 + b'\033[0m' else: tok = lib.unescape(tok) buffer.write(tok) @@ -161,12 +171,20 @@ def query(cmd, *args): else: return decode(script('get-file', version, path)) + elif cmd == 'family': + # Get the family of a given file + + filename = args[0] + + return lib.getFileFamily(filename) + elif cmd == 'ident': # Returns identifier search results version = args[0] ident = args[1] + family = args[2] symbol_definitions = [] symbol_references = [] @@ -196,9 +214,9 @@ def query(cmd, *args): # values. Therefore, we can sequentially step through the defs, refs, # and docs for each file in a version. - def_idx, def_type, def_line = next(defs_this_ident) - ref_idx, ref_lines = next(refs) - doc_idx, doc_line = next(docs) + def_idx, def_type, def_line, def_family = next(defs_this_ident) + ref_idx, ref_lines, ref_family = next(refs) + doc_idx, doc_line, doc_family = next(docs) dBuf = [] rBuf = [] @@ -207,19 +225,21 @@ def query(cmd, *args): for file_idx, file_path in files_this_version: # Advance defs, refs, and docs to the current file while def_idx < file_idx: - def_idx, def_type, def_line = next(defs_this_ident) + def_idx, def_type, def_line, def_family = next(defs_this_ident) while ref_idx < file_idx: - ref_idx, ref_lines = next(refs) + ref_idx, ref_lines, ref_family = next(refs) while doc_idx < file_idx: - doc_idx, doc_line = next(docs) + doc_idx, doc_line, doc_family = next(docs) # Copy information about this identifier into dBuf, rBuf, and docBuf. while def_idx == file_idx: - dBuf.append((file_path, def_type, def_line)) - def_idx, def_type, def_line = next(defs_this_ident) + if def_family == family: + dBuf.append((file_path, def_type, def_line)) + def_idx, def_type, def_line, def_family = next(defs_this_ident) if ref_idx == file_idx: - rBuf.append((file_path, ref_lines)) + if lib.compatibleFamily(family, ref_family): + rBuf.append((file_path, ref_lines)) if doc_idx == file_idx: # TODO should this be a `while`? docBuf.append((file_path, doc_line)) @@ -239,8 +259,8 @@ def query(cmd, *args): else: return('Unknown subcommand: ' + cmd + '\n') -def cmd_ident(version, ident, **kwargs): - symbol_definitions, symbol_references, symbol_doccomments = query("ident", version, ident) +def cmd_ident(version, ident, family, **kwargs): + symbol_definitions, symbol_references, symbol_doccomments = query("ident", version, ident, family) print("Symbol Definitions:") for symbol_definition in symbol_definitions: print(symbol_definition) @@ -266,6 +286,7 @@ def cmd_file(version, path, **kwargs): ident_subparser = subparsers.add_parser('ident', help="Get definitions and references of an identifier") ident_subparser.add_argument('ident', type=str, help="The name of the identifier") + ident_subparser.add_argument('family', type=str, help="The file family requested") ident_subparser.set_defaults(func=cmd_ident) file_subparser = subparsers.add_parser('file', help="Get a source file") From 962d6fb539de96c66dacef3d261f3da6c12871a6 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Fri, 22 May 2020 10:11:59 +0200 Subject: [PATCH 079/529] web interface: Add support for Kconfig and Devicetree idents Keep old urls compatibility by settings family to C when nothing is given. Add a select element to select in which file family we want to search an ident. Update filters to support families and to handle Kconfig idents properly. Signed-off-by: Maxime Chretien --- http/filters/barebox.py | 2 +- http/filters/commonkconfig.py | 5 +++++ http/filters/coreboot.py | 2 +- http/filters/ident.py | 4 ++-- http/filters/kconfig.py | 2 +- http/filters/kconfigidents.py | 27 +++++++++++++++++++++++++++ http/filters/linux.py | 2 +- http/filters/makefilekconfig.py | 22 ++++++++++++++++++++++ http/filters/qemu.py | 2 +- http/filters/u-boot.py | 2 +- http/filters/zephyr.py | 2 +- http/style.css | 13 +++++++++++++ http/web.py | 24 ++++++++++++++++-------- templates/layout.html | 8 +++++++- 14 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 http/filters/commonkconfig.py create mode 100644 http/filters/kconfigidents.py create mode 100644 http/filters/makefilekconfig.py diff --git a/http/filters/barebox.py b/http/filters/barebox.py index 4c39138d..42d5bde0 100644 --- a/http/filters/barebox.py +++ b/http/filters/barebox.py @@ -1,7 +1,7 @@ # Elixir Python definitions for Barebox exec(open('dtsi.py').read()) -exec(open('kconfig.py').read()) +exec(open('commonkconfig.py').read()) exec(open('cpppathinc.py').read()) exec(open('makefileo.py').read()) exec(open('makefiledtb.py').read()) diff --git a/http/filters/commonkconfig.py b/http/filters/commonkconfig.py new file mode 100644 index 00000000..18378c8f --- /dev/null +++ b/http/filters/commonkconfig.py @@ -0,0 +1,5 @@ +# Common filters for Kconfig + +exec(open('kconfig.py').read()) +exec(open('kconfigidents.py').read()) +exec(open('makefilekconfig.py').read()) diff --git a/http/filters/coreboot.py b/http/filters/coreboot.py index 4f610889..b295548e 100644 --- a/http/filters/coreboot.py +++ b/http/filters/coreboot.py @@ -1,4 +1,4 @@ # Elixir Python definitions for Coreboot exec(open('dtsi.py').read()) -exec(open('kconfig.py').read()) +exec(open('commonkconfig.py').read()) diff --git a/http/filters/ident.py b/http/filters/ident.py index 7dd2cfc2..2c818718 100644 --- a/http/filters/ident.py +++ b/http/filters/ident.py @@ -8,11 +8,11 @@ def keep_idents(m): def replace_idents(m): i = idents[decode_number(m.group(1)) - 1] - return ''+i+'' + return ''+i+'' ident_filters = { 'case': 'any', - 'prerex': '\033\[31m(.*?)\033\[0m', + 'prerex': '\033\[31m(?!CONFIG_)(.*?)\033\[0m', 'prefunc': keep_idents, 'postrex': '__KEEPIDENTS__([A-J]+)', 'postfunc': replace_idents diff --git a/http/filters/kconfig.py b/http/filters/kconfig.py index d993fce4..1885c0a9 100644 --- a/http/filters/kconfig.py +++ b/http/filters/kconfig.py @@ -13,7 +13,7 @@ def replace_kconfig(m): kconfig_filters = { 'case': 'filename', 'match': {'Kconfig'}, - 'prerex': '^(\s*)(source)(\s*)\"(.*?)\"', + 'prerex': '^(\s*)(source)(\s*)\"([\w/_-]*)\"', 'prefunc': keep_kconfig, 'postrex': '__KEEPKCONFIG__([A-J]+)', 'postfunc': replace_kconfig diff --git a/http/filters/kconfigidents.py b/http/filters/kconfigidents.py new file mode 100644 index 00000000..20036bdb --- /dev/null +++ b/http/filters/kconfigidents.py @@ -0,0 +1,27 @@ +# Filter for kconfig identifier links + +kconfigidents = [] + +def keep_kconfigidents(m): + kconfigidents.append(m.group(1)) + return '__KEEPKCONFIGIDENTS__' + encode_number(len(kconfigidents)) + +def replace_kconfigidents(m): + i = kconfigidents[decode_number(m.group(1)) - 1] + + n = i + #Remove the CONFIG_ when we are in a Kconfig file + if family == 'K': + n = n[7:] + + return ''+n+'' + +kconfigident_filters = { + 'case': 'any', + 'prerex': '\033\[31m(?=CONFIG_)(.*?)\033\[0m', + 'prefunc': keep_kconfigidents, + 'postrex': '__KEEPKCONFIGIDENTS__([A-J]+)', + 'postfunc': replace_kconfigidents + } + +filters.append(kconfigident_filters) diff --git a/http/filters/linux.py b/http/filters/linux.py index 12d0866e..a7dabb37 100644 --- a/http/filters/linux.py +++ b/http/filters/linux.py @@ -1,7 +1,7 @@ # Elixir Python definitions for Linux exec(open('dtsi.py').read()) -exec(open('kconfig.py').read()) +exec(open('commonkconfig.py').read()) exec(open('makefileo.py').read()) exec(open('makefiledtb.py').read()) exec(open('makefiledir.py').read()) diff --git a/http/filters/makefilekconfig.py b/http/filters/makefilekconfig.py new file mode 100644 index 00000000..10ba3248 --- /dev/null +++ b/http/filters/makefilekconfig.py @@ -0,0 +1,22 @@ +# Filters for Kconfig used in Makefiles + +makefilekconfig = [] + +def keep_makefilekconfig(m): + makefilekconfig.append(m.group(1)) + return '$(__KEEPMAKEFILEKCONFIG__' + encode_number(len(makefilekconfig)) + ')' + +def replace_makefilekconfig(m): + i = makefilekconfig[decode_number(m.group(1)) - 1] + return ''+i+'' + +makefilekconfig_filters = { + 'case': 'filename', + 'match': {'Makefile'}, + 'prerex': '($\(CONFIG_\w+\))', + 'prefunc': keep_makefilekconfig, + 'postrex': '__KEEPMAKEFILEKCONFIG__([A-J]+)', + 'postfunc': replace_makefilekconfig + } + +filters.append(makefilekconfig_filters) diff --git a/http/filters/qemu.py b/http/filters/qemu.py index 3d4f311f..b09f43db 100644 --- a/http/filters/qemu.py +++ b/http/filters/qemu.py @@ -1,3 +1,3 @@ # Elixir Python definitions for qemu -exec(open('kconfig.py').read()) +exec(open('commonkconfig.py').read()) diff --git a/http/filters/u-boot.py b/http/filters/u-boot.py index 784ee914..060faa5a 100644 --- a/http/filters/u-boot.py +++ b/http/filters/u-boot.py @@ -1,7 +1,7 @@ # Elixir Python definitions for U-Boot exec(open('dtsi.py').read()) -exec(open('kconfig.py').read()) +exec(open('commonkconfig.py').read()) exec(open('cpppathinc.py').read()) exec(open('makefileo.py').read()) exec(open('makefiledtb.py').read()) diff --git a/http/filters/zephyr.py b/http/filters/zephyr.py index d570f6bf..4e857ccb 100644 --- a/http/filters/zephyr.py +++ b/http/filters/zephyr.py @@ -1,5 +1,5 @@ # Elixir Python definitions for Zephyr exec(open('dtsi.py').read()) -exec(open('kconfig.py').read()) +exec(open('commonkconfig.py').read()) exec(open('cpppathinc.py').read()) diff --git a/http/style.css b/http/style.css index 855beaeb..f03556ad 100644 --- a/http/style.css +++ b/http/style.css @@ -253,6 +253,10 @@ h2 { padding: 0.5em; padding-top: 0; } +.search form { + display: flex; + flex-direction: row; +} .search button:focus, .search button:hover { color: #000; @@ -262,10 +266,19 @@ h2 { color: #000; background: #ddd; padding-right: 3em; + min-width: 0; + flex: 4; } .search input:focus { background: #eee; } +.search select { + font-size: 0.9em; + padding: 0.45em; + margin: 0; + min-width: 0; + flex: 1; +} .filter { padding: 0.5em; diff --git a/http/web.py b/http/web.py index 48e7294f..8b985db7 100755 --- a/http/web.py +++ b/http/web.py @@ -48,14 +48,18 @@ def print(arg, end='\n'): url = os.environ.get('REQUEST_URI') or os.environ.get('SCRIPT_URL') # Split the URL into its components (project, version, cmd, arg) -m = search('^/([^/]*)/([^/]*)/([^/]*)(.*)$', url) +m = search('^/([^/]*)/([^/]*)(?:/([^/]))?/([^/]*)(.*)$', url) if m: project = m.group(1) version = m.group(2) version_decoded = parse.unquote(version) - cmd = m.group(3) - arg = m.group(4) + family = m.group(3) + cmd = m.group(4) + arg = m.group(5) + + if family == None: + family = 'C' basedir = os.environ['LXR_PROJ_DIR'] datadir = basedir + '/' + project + '/data' @@ -80,15 +84,16 @@ def print(arg, end='\n'): ident = arg[1:] form = cgi.FieldStorage() ident2 = form.getvalue('i') + family2 = form.getvalue('f') if ident == '' and ident2: status = 302 ident2 = parse.quote(ident2.strip()) - location = '/'+project+'/'+version+'/ident/'+ident2 + location = '/'+project+'/'+version+'/'+family2+'/ident/'+ident2 else: mode = 'ident' if not(ident and search('^[A-Za-z0-9_-]*$', ident)): ident = '' - url = 'ident/'+ident + url = family + '/ident/' + ident else: status = 400 else: @@ -132,6 +137,7 @@ def print(arg, end='\n'): 'project': project, 'projects': projects, 'ident': ident, + 'family': family, 'breadcrumb': '/' } @@ -249,9 +255,11 @@ def print(arg, end='\n'): import pygments.lexers import pygments.formatters - filename, extension = os.path.splitext(path) + fname = os.path.basename(path) + filename, extension = os.path.splitext(fname) extension = extension[1:].lower() - filename = os.path.basename(filename) + family = query('family', fname) + data['family'] = family # Source common filter definitions os.chdir('filters') @@ -302,7 +310,7 @@ def print(arg, end='\n'): elif mode == 'ident': data['title'] = ident+' identifier - '+title_suffix - symbol_definitions, symbol_references, symbol_doccomments_UNUSED = query('ident', tag, ident) + symbol_definitions, symbol_references, symbol_doccomments_UNUSED = query('ident', tag, ident, family) print('
') if len(symbol_definitions): diff --git a/templates/layout.html b/templates/layout.html index 63f7b3c4..682a350b 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -23,7 +23,13 @@ {{breadcrumb}}
From 39097c7d5d1cd2c657bc5947e1ed4b6aac751c66 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 25 Jun 2020 14:05:18 +0200 Subject: [PATCH 190/529] http/filters: Add links to Kconfig in defconfig files Signed-off-by: Maxime Chretien --- http/filters/commonkconfig.py | 1 + http/filters/defconfig.py | 23 +++++++++++++++++++++++ http/web.py | 6 ++++-- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 http/filters/defconfig.py diff --git a/http/filters/commonkconfig.py b/http/filters/commonkconfig.py index 778e0588..b8fbd1df 100644 --- a/http/filters/commonkconfig.py +++ b/http/filters/commonkconfig.py @@ -2,3 +2,4 @@ exec(open('kconfig.py').read()) exec(open('kconfigidents.py').read()) +exec(open('defconfig.py').read()) diff --git a/http/filters/defconfig.py b/http/filters/defconfig.py new file mode 100644 index 00000000..4a0fa38d --- /dev/null +++ b/http/filters/defconfig.py @@ -0,0 +1,23 @@ +# Filter for kconfig identifier in defconfigs + +defconfigidents = [] + +def keep_defconfigidents(m): + defconfigidents.append(m.group(1)) + return '__KEEPDEFCONFIGIDENTS__' + encode_number(len(defconfigidents)) + +def replace_defconfigidents(m): + i = defconfigidents[decode_number(m.group(1)) - 1] + + return ''+i+'' + +defconfigident_filters = { + 'case': 'filename_extension', + 'match': {'defconfig'}, + 'prerex': '(CONFIG_[\w]+)', + 'prefunc': keep_defconfigidents, + 'postrex': '__KEEPDEFCONFIGIDENTS__([A-J]+)', + 'postfunc': replace_defconfigidents + } + +filters.append(defconfigident_filters) diff --git a/http/web.py b/http/web.py index 130fedc8..f7bbc2e4 100755 --- a/http/web.py +++ b/http/web.py @@ -284,7 +284,8 @@ def print(arg, end='\n'): if (c == 'any' or (c == 'filename' and filename in f['match']) or (c == 'extension' and extension in f['match']) or - (c == 'path' and fdir.startswith(tuple(f['match'])))): + (c == 'path' and fdir.startswith(tuple(f['match']))) or + (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): apply_filter = True @@ -314,7 +315,8 @@ def print(arg, end='\n'): if (c == 'any' or (c == 'filename' and filename in f['match']) or (c == 'extension' and extension in f['match']) or - (c == 'path' and fdir.startswith(tuple(f['match'])))): + (c == 'path' and fdir.startswith(tuple(f['match']))) or + (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): result = sub(f ['postrex'], f ['postfunc'], result) From 82be89b0a6825cc4c747e4dcbda4bc339f8d0e13 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Mon, 29 Jun 2020 10:23:44 +0200 Subject: [PATCH 191/529] docker: Update debian Dockerfile Signed-off-by: Maxime Chretien --- docker/debian/Dockerfile | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 1df0306f..229b8a92 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -16,14 +16,26 @@ RUN \ apt-get -y install \ python3 \ python3-jinja2 \ - python3-pygments \ python3-bsddb3 \ python3-falcon \ - exuberant-ctags \ + python3-pytest \ perl \ git \ apache2 \ - libapache2-mod-wsgi-py3 + libapache2-mod-wsgi-py3 \ + libjansson4 + +RUN \ + wget https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb + +RUN \ + dpkg -i universal-ctags_0+git20200526-0ubuntu1_amd64.deb + +RUN \ + wget https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl + +RUN \ + pip3 install Pygments-2.6.1.elixir-py3-none-any.whl RUN \ git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ @@ -47,7 +59,7 @@ RUN \ # apache elixir config, see elixir README # make apache less stricter about cgitb spam headers RUN \ - echo PERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9odHRwLz4KICAgIE9wdGlvbnMgK0V4ZWNDR0kKICAgIEFsbG93T3ZlcnJpZGUgTm9uZQogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KQWRkSGFuZGxlciBjZ2ktc2NyaXB0IC5weQo8VmlydHVhbEhvc3QgKjo4MD4KICAgIFNlcnZlck5hbWUgTVlfTE9DQUxfSVAKICAgIERvY3VtZW50Um9vdCAvdXNyL2xvY2FsL2VsaXhpci9odHRwCiAgICBSZXdyaXRlRW5naW5lIG9uCiAgICBSZXdyaXRlUnVsZSAiXi8kIiAiL2xpbnV4L2xhdGVzdC9zb3VyY2UiIFtSXQogICAgUmV3cml0ZVJ1bGUgIl4vLiovKHNvdXJjZXxpZGVudHxzZWFyY2gpIiAiL3dlYi5weSIgW1BUXQo8L1ZpcnR1YWxIb3N0Pgo= | base64 -d > /etc/apache2/sites-available/000-default.conf && \ + echo PERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9odHRwLz4KICAgIE9wdGlvbnMgK0V4ZWNDR0kKICAgIEFsbG93T3ZlcnJpZGUgTm9uZQogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KPERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9hcGkvPgogICAgU2V0SGFuZGxlciB3c2dpLXNjcmlwdAogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KQWRkSGFuZGxlciBjZ2ktc2NyaXB0IC5weQo8VmlydHVhbEhvc3QgKjo4MD4KICAgIFNlcnZlck5hbWUgTVlfTE9DQUxfSVAKICAgIERvY3VtZW50Um9vdCAvdXNyL2xvY2FsL2VsaXhpci9odHRwCiAgICBXU0dJU2NyaXB0QWxpYXMgL2FwaSAvdXNyL2xvY2FsL2VsaXhpci9hcGkvYXBpLnB5CiAgICBBbGxvd0VuY29kZWRTbGFzaGVzIE9uCiAgICBSZXdyaXRlRW5naW5lIG9uCiAgICBSZXdyaXRlUnVsZSAiXi8kIiAiL2xpbnV4L2xhdGVzdC9zb3VyY2UiIFtSXQogICAgUmV3cml0ZVJ1bGUgIl4vKD8hYXBpfGFjcCkuKi8oc291cmNlfGlkZW50fHNlYXJjaCkiICIvd2ViLnB5IiBbUFRdCiAgICBSZXdyaXRlUnVsZSAiXi9hY3AiICIvYXV0b2NvbXBsZXRlLnB5IiBbUFRdCjwvVmlydHVhbEhvc3Q+Cg== | base64 -d > /etc/apache2/sites-available/000-default.conf && \ echo -e "\nHttpProtocolOptions Unsafe" >> /etc/apache2/apache.conf && \ a2enmod cgi rewrite From 06fd53762177a626afcda2a98496b38544d56ac4 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 2 Jul 2020 12:03:36 +0200 Subject: [PATCH 192/529] docker: Add missing dependencies for debian Signed-off-by: Maxime Chretien --- docker/debian/Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 229b8a92..163ceaec 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -15,6 +15,7 @@ RUN \ apt-get update && \ apt-get -y install \ python3 \ + python3-pip \ python3-jinja2 \ python3-bsddb3 \ python3-falcon \ @@ -23,7 +24,9 @@ RUN \ git \ apache2 \ libapache2-mod-wsgi-py3 \ - libjansson4 + libjansson4 \ + libyaml-0-2 \ + wget RUN \ wget https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb From 8e7691d0916e6d16512554b0a312b8b9ff6bfae6 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 2 Jul 2020 16:10:29 +0200 Subject: [PATCH 193/529] docker: Update CentOS dockerfile Signed-off-by: Maxime Chretien --- docker/centos/Dockerfile | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile index 5e80003a..2ab56f29 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos/Dockerfile @@ -27,12 +27,29 @@ RUN \ RUN \ yum install -y \ python36-jinja2 \ - python36-pygments \ python36-bsddb3 \ - global-ctags \ - git + python36-pip \ + python36-falcon \ + python36-pytest \ + rh-python36-mod_wsgi \ + perl-autodie \ + jansson \ + libyaml \ + git \ + wget # httpd & perl is inherited from upstream openshift image +RUN \ + wget https://bootlin.com/pub/elixir/universal-ctags-0+git~20e934e3-1.6.x86_64.rpm + +RUN \ + rpm -iv universal-ctags-0+git~20e934e3-1.6.x86_64.rpm + +RUN \ + wget https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl + +RUN \ + pip3 install Pygments-2.6.1.elixir-py3-none-any.whl RUN \ git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ @@ -53,21 +70,7 @@ RUN \ # apache elixir config, see elixir README # apache HttpProtolOptions workaround RUN \ - echo $'\n\ - Options +ExecCGI\n\ - AllowOverride None\n\ - Require all granted\n\ - SetEnv PYTHONIOENCODING utf-8\n\ - SetEnv LXR_PROJ_DIR /srv/elixir-data\n\ -\n\ -AddHandler cgi-script .py\n\ -\n\ - ServerName MY_LOCAL_IP\n\ - DocumentRoot /usr/local/elixir/http\n\ - RewriteEngine on\n\ - RewriteRule "^/$" "/$PROJECT/latest/source" [R]\n\ - RewriteRule "^/.*/(source|ident|search)" "/web.py" [PT]\n\ -' > /etc/httpd/conf.d/000-elixir.conf \ + echo PERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9odHRwLz4KICAgIE9wdGlvbnMgK0V4ZWNDR0kKICAgIEFsbG93T3ZlcnJpZGUgTm9uZQogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KPERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9hcGkvPgogICAgU2V0SGFuZGxlciB3c2dpLXNjcmlwdAogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KQWRkSGFuZGxlciBjZ2ktc2NyaXB0IC5weQo8VmlydHVhbEhvc3QgKjo4MDgwPgogICAgU2VydmVyTmFtZSBNWV9MT0NBTF9JUAogICAgRG9jdW1lbnRSb290IC91c3IvbG9jYWwvZWxpeGlyL2h0dHAKICAgIFdTR0lTY3JpcHRBbGlhcyAvYXBpIC91c3IvbG9jYWwvZWxpeGlyL2FwaS9hcGkucHkKICAgIEFsbG93RW5jb2RlZFNsYXNoZXMgT24KICAgIFJld3JpdGVFbmdpbmUgb24KICAgIFJld3JpdGVSdWxlICJeLyQiICIvbGludXgvbGF0ZXN0L3NvdXJjZSIgW1JdCiAgICBSZXdyaXRlUnVsZSAiXi8oPyFhcGl8YWNwKS4qLyhzb3VyY2V8aWRlbnR8c2VhcmNoKSIgIi93ZWIucHkiIFtQVF0KICAgIFJld3JpdGVSdWxlICJeL2FjcCIgIi9hdXRvY29tcGxldGUucHkiIFtQVF0KPC9WaXJ0dWFsSG9zdD4= | base64 -d > /etc/httpd/conf.d/000-elixir.conf \ && echo -e "\nHttpProtocolOptions Unsafe" >> /etc/httpd/conf/httpd.conf # a2enmod cgi rewrite - enabled by default in RHEL/centos From f32b5b21f2ddbf3e64b173e9bf5ebd355f93cc93 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 2 Jul 2020 16:33:57 +0200 Subject: [PATCH 194/529] README: Update CentOS dependencies Signed-off-by: Maxime Chretien --- README.adoc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index d47d6952..99bdce50 100644 --- a/README.adoc +++ b/README.adoc @@ -72,7 +72,7 @@ For RedHat/CentOS ____ ---- -yum install python36-jinja2 python36-pygments python36-bsddb3 python3-falcon global-ctags git httpd +yum install python36-pip python36-pytest python36-jinja2 python36-bsddb3 python36-falcon git httpd perl perl-autodie jansson libyaml rh-python36-mod_wsgi ---- ____ @@ -80,7 +80,7 @@ For Debian ____ ---- -sudo apt install python3 python3-jinja2 python3-pygments python3-bsddb3 python3-falcon python3-pytest universal-ctags perl git apache2 libapache2-mod-wsgi-py3 libjansson4 +sudo apt install python3 python3-jinja2 python3-bsddb3 python3-falcon python3-pytest universal-ctags perl git apache2 libapache2-mod-wsgi-py3 libjansson4 ---- To enable the REST API, follow the installation instructions on https://github.com/GrahamDumpleton/mod_wsgi[`mod_wsgi`] @@ -116,11 +116,14 @@ The changes have been sent upstream and should appear in future distributions. In the meantime, you can either rebuild this package from its source on https://github.com/universal-ctags/ctags[GitHub], or download this -https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb[binary package] +https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb[deb binary package] +or https://bootlin.com/pub/elixir/universal-ctags-0+git~20e934e3-1.6.x86_64.rpm[rpm binary package] and install it as follows: ---- sudo dpkg -i universal-ctags_0+git20200526-0ubuntu1_amd64.deb +or +sudo rpm -iv universal-ctags-0+git~20e934e3-1.6.x86_64.rpm ---- Then tell `apt` to hold this package and block future updates of the normal package: From be5e6fd6b4a46286bb11e6c95b78b024c2b3d6d6 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 9 Jul 2020 17:04:37 +0200 Subject: [PATCH 195/529] web / lib : Improve family checking This fixes https://github.com/bootlin/elixir/issues/175 Signed-off-by: Maxime Chretien --- http/web.py | 2 +- lib.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/http/web.py b/http/web.py index f7bbc2e4..dbf1f821 100755 --- a/http/web.py +++ b/http/web.py @@ -58,7 +58,7 @@ def print(arg, end='\n'): cmd = m.group(4) arg = m.group(5) - if family == None: + if not lib.validFamily(family): family = 'C' search_family = 'A' diff --git a/lib.py b/lib.py index 7b218b3a..6c8f7f95 100755 --- a/lib.py +++ b/lib.py @@ -196,6 +196,12 @@ def getDataDir(): def currentProject(): return os.path.basename(os.path.dirname(getDataDir())) +# List all families supported by Elixir +families = ['A', 'B', 'C', 'D', 'K', 'M'] + +def validFamily(family): + return family in families + def getFileFamily(filename): name, ext = os.path.splitext(filename) From de1d9f5f8e00d1096fb977f67ea8ee7540e43235 Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 9 Jul 2020 17:10:19 +0200 Subject: [PATCH 196/529] web.py : Allow the use of lowercase family names This improve the fix for https://github.com/bootlin/elixir/issues/175 m.group(3) can be None but str convert it to 'None' so upper() is always defined and the resulting 'NONE' is discarded by the next test. Signed-off-by: Maxime Chretien --- http/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/web.py b/http/web.py index dbf1f821..ce247f08 100755 --- a/http/web.py +++ b/http/web.py @@ -54,7 +54,7 @@ def print(arg, end='\n'): project = m.group(1) version = m.group(2) version_decoded = parse.unquote(version) - family = m.group(3) + family = str(m.group(3)).upper() cmd = m.group(4) arg = m.group(5) From 371b03135a2795dce67a7e0be4be2171e4067bda Mon Sep 17 00:00:00 2001 From: Maxime Chretien Date: Thu, 9 Jul 2020 17:24:58 +0200 Subject: [PATCH 197/529] web.py : Check family validity in html form results This fixes https://github.com/bootlin/elixir/issues/176 Also add missing lib import Signed-off-by: Maxime Chretien --- http/web.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/http/web.py b/http/web.py index ce247f08..eda59e28 100755 --- a/http/web.py +++ b/http/web.py @@ -34,6 +34,10 @@ def print(arg, end='\n'): import re from re import search, sub +import sys +sys.path = [ sys.path[0] + '/..' ] + sys.path +from lib import validFamily + # Create /tmp/elixir-errors if not existing yet (could happen after a reboot) errdir = '/tmp/elixir-errors' @@ -58,7 +62,7 @@ def print(arg, end='\n'): cmd = m.group(4) arg = m.group(5) - if not lib.validFamily(family): + if not validFamily(family): family = 'C' search_family = 'A' @@ -88,7 +92,10 @@ def print(arg, end='\n'): ident = arg[1:] form = cgi.FieldStorage() ident2 = form.getvalue('i') - family2 = form.getvalue('f') + family2 = str(form.getvalue('f')).upper() + if not validFamily(family2): + family2 = 'C' + if ident == '' and ident2: status = 302 ident2 = parse.quote(ident2.strip()) @@ -124,8 +131,6 @@ def print(arg, end='\n'): break projects.sort() -import sys -sys.path = [ sys.path[0] + '/..' ] + sys.path from query import query dts_comp_support = query('dts-comp') From c04e9e306502501d24a41a4890945caeadb4b4d7 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Tue, 18 Aug 2020 12:01:56 +0200 Subject: [PATCH 198/529] Remove trailing ";" Signed-off-by: Michael Opdenacker --- update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update.py b/update.py index 4169b297..ed297917 100755 --- a/update.py +++ b/update.py @@ -230,7 +230,7 @@ def update_definitions(self, idxes): hash = db.hash.get(idx) filename = db.file.get(idx) - family = lib.getFileFamily(filename); + family = lib.getFileFamily(filename) if family in [None, 'M']: continue lines = scriptLines('parse-defs', hash, filename, family) From 09f37db41f33173d2acdaaaea1dd4c70cd83a932 Mon Sep 17 00:00:00 2001 From: AlphaHot <58410019+AlphaHot@users.noreply.github.com> Date: Wed, 19 Aug 2020 14:57:59 +0500 Subject: [PATCH 199/529] web : Add autofocus when entering a search --- templates/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/layout.html b/templates/layout.html index 685fa461..86788b1f 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -33,7 +33,7 @@ - + From f1b55966893ce3505525691b2f5fdba0d900e6d1 Mon Sep 17 00:00:00 2001 From: Kezi Date: Thu, 17 Sep 2020 22:32:03 +0200 Subject: [PATCH 200/529] Shrink .hilight a background color --- http/style.css | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/http/style.css b/http/style.css index 71505293..c405b045 100644 --- a/http/style.css +++ b/http/style.css @@ -732,9 +732,7 @@ h2 { .highlight a { color: inherit; font-weight: 700; - background-color: #f4f6ff; - -webkit-box-shadow: 0 0 0 1px #f4f6ff; - box-shadow: 0 0 0 1px #f4f6ff; + background: linear-gradient(to bottom, #0000 10%, #f4f6ff 10%, #f4f6ff 90%, #0000 90%); border-radius: 0.2em; } .highlight a:hover { From 2c77af789500960c74e45e499ea584c8c4479757 Mon Sep 17 00:00:00 2001 From: mlern Date: Sun, 27 Sep 2020 11:07:27 -0700 Subject: [PATCH 201/529] Fix a misspelling with the path to store elixir-data --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 99bdce50..06ab44b9 100644 --- a/README.adoc +++ b/README.adoc @@ -167,7 +167,7 @@ And then run `source /etc/profile`. First clone the master tree released by Linus Torvalds: ---- -cd /pathy/elixir-data/linux +cd /path/elixir-data/linux git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git repo ---- From 9d8b213862e5b7055b8fbda2550d48dc4cf0c76a Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 23 May 2020 18:15:00 -0400 Subject: [PATCH 202/529] Add test file t/tree/include/linux/memblock.h Per . Copied from Linux 5.6, at . This file is GPL-2.0-or-later. --- t/tree/include/linux/memblock.h | 571 ++++++++++++++++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 t/tree/include/linux/memblock.h diff --git a/t/tree/include/linux/memblock.h b/t/tree/include/linux/memblock.h new file mode 100644 index 00000000..079d17d9 --- /dev/null +++ b/t/tree/include/linux/memblock.h @@ -0,0 +1,571 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_MEMBLOCK_H +#define _LINUX_MEMBLOCK_H +#ifdef __KERNEL__ + +/* + * Logical memory blocks. + * + * Copyright (C) 2001 Peter Bergner, IBM Corp. + */ + +#include +#include +#include + +extern unsigned long max_low_pfn; +extern unsigned long min_low_pfn; + +/* + * highest page + */ +extern unsigned long max_pfn; +/* + * highest possible page + */ +extern unsigned long long max_possible_pfn; + +/** + * enum memblock_flags - definition of memory region attributes + * @MEMBLOCK_NONE: no special request + * @MEMBLOCK_HOTPLUG: hotpluggable region + * @MEMBLOCK_MIRROR: mirrored region + * @MEMBLOCK_NOMAP: don't add to kernel direct mapping + */ +enum memblock_flags { + MEMBLOCK_NONE = 0x0, /* No special request */ + MEMBLOCK_HOTPLUG = 0x1, /* hotpluggable region */ + MEMBLOCK_MIRROR = 0x2, /* mirrored region */ + MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */ +}; + +/** + * struct memblock_region - represents a memory region + * @base: physical address of the region + * @size: size of the region + * @flags: memory region attributes + * @nid: NUMA node id + */ +struct memblock_region { + phys_addr_t base; + phys_addr_t size; + enum memblock_flags flags; +#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP + int nid; +#endif +}; + +/** + * struct memblock_type - collection of memory regions of certain type + * @cnt: number of regions + * @max: size of the allocated array + * @total_size: size of all regions + * @regions: array of regions + * @name: the memory type symbolic name + */ +struct memblock_type { + unsigned long cnt; + unsigned long max; + phys_addr_t total_size; + struct memblock_region *regions; + char *name; +}; + +/** + * struct memblock - memblock allocator metadata + * @bottom_up: is bottom up direction? + * @current_limit: physical address of the current allocation limit + * @memory: usabe memory regions + * @reserved: reserved memory regions + * @physmem: all physical memory + */ +struct memblock { + bool bottom_up; /* is bottom up direction? */ + phys_addr_t current_limit; + struct memblock_type memory; + struct memblock_type reserved; +#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP + struct memblock_type physmem; +#endif +}; + +extern struct memblock memblock; +extern int memblock_debug; + +#ifndef CONFIG_ARCH_KEEP_MEMBLOCK +#define __init_memblock __meminit +#define __initdata_memblock __meminitdata +void memblock_discard(void); +#else +#define __init_memblock +#define __initdata_memblock +static inline void memblock_discard(void) {} +#endif + +#define memblock_dbg(fmt, ...) \ + if (memblock_debug) printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) + +phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end, + phys_addr_t size, phys_addr_t align); +void memblock_allow_resize(void); +int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid); +int memblock_add(phys_addr_t base, phys_addr_t size); +int memblock_remove(phys_addr_t base, phys_addr_t size); +int memblock_free(phys_addr_t base, phys_addr_t size); +int memblock_reserve(phys_addr_t base, phys_addr_t size); +#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP +int memblock_physmem_add(phys_addr_t base, phys_addr_t size); +#endif +void memblock_trim_memory(phys_addr_t align); +bool memblock_overlaps_region(struct memblock_type *type, + phys_addr_t base, phys_addr_t size); +int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size); +int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size); +int memblock_mark_mirror(phys_addr_t base, phys_addr_t size); +int memblock_mark_nomap(phys_addr_t base, phys_addr_t size); +int memblock_clear_nomap(phys_addr_t base, phys_addr_t size); + +unsigned long memblock_free_all(void); +void reset_node_managed_pages(pg_data_t *pgdat); +void reset_all_zones_managed_pages(void); + +/* Low level functions */ +void __next_mem_range(u64 *idx, int nid, enum memblock_flags flags, + struct memblock_type *type_a, + struct memblock_type *type_b, phys_addr_t *out_start, + phys_addr_t *out_end, int *out_nid); + +void __next_mem_range_rev(u64 *idx, int nid, enum memblock_flags flags, + struct memblock_type *type_a, + struct memblock_type *type_b, phys_addr_t *out_start, + phys_addr_t *out_end, int *out_nid); + +void __next_reserved_mem_region(u64 *idx, phys_addr_t *out_start, + phys_addr_t *out_end); + +void __memblock_free_late(phys_addr_t base, phys_addr_t size); + +/** + * for_each_mem_range - iterate through memblock areas from type_a and not + * included in type_b. Or just type_a if type_b is NULL. + * @i: u64 used as loop variable + * @type_a: ptr to memblock_type to iterate + * @type_b: ptr to memblock_type which excludes from the iteration + * @nid: node selector, %NUMA_NO_NODE for all nodes + * @flags: pick from blocks based on memory attributes + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + */ +#define for_each_mem_range(i, type_a, type_b, nid, flags, \ + p_start, p_end, p_nid) \ + for (i = 0, __next_mem_range(&i, nid, flags, type_a, type_b, \ + p_start, p_end, p_nid); \ + i != (u64)ULLONG_MAX; \ + __next_mem_range(&i, nid, flags, type_a, type_b, \ + p_start, p_end, p_nid)) + +/** + * for_each_mem_range_rev - reverse iterate through memblock areas from + * type_a and not included in type_b. Or just type_a if type_b is NULL. + * @i: u64 used as loop variable + * @type_a: ptr to memblock_type to iterate + * @type_b: ptr to memblock_type which excludes from the iteration + * @nid: node selector, %NUMA_NO_NODE for all nodes + * @flags: pick from blocks based on memory attributes + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + */ +#define for_each_mem_range_rev(i, type_a, type_b, nid, flags, \ + p_start, p_end, p_nid) \ + for (i = (u64)ULLONG_MAX, \ + __next_mem_range_rev(&i, nid, flags, type_a, type_b,\ + p_start, p_end, p_nid); \ + i != (u64)ULLONG_MAX; \ + __next_mem_range_rev(&i, nid, flags, type_a, type_b, \ + p_start, p_end, p_nid)) + +/** + * for_each_reserved_mem_region - iterate over all reserved memblock areas + * @i: u64 used as loop variable + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * + * Walks over reserved areas of memblock. Available as soon as memblock + * is initialized. + */ +#define for_each_reserved_mem_region(i, p_start, p_end) \ + for (i = 0UL, __next_reserved_mem_region(&i, p_start, p_end); \ + i != (u64)ULLONG_MAX; \ + __next_reserved_mem_region(&i, p_start, p_end)) + +static inline bool memblock_is_hotpluggable(struct memblock_region *m) +{ + return m->flags & MEMBLOCK_HOTPLUG; +} + +static inline bool memblock_is_mirror(struct memblock_region *m) +{ + return m->flags & MEMBLOCK_MIRROR; +} + +static inline bool memblock_is_nomap(struct memblock_region *m) +{ + return m->flags & MEMBLOCK_NOMAP; +} + +#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP +int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn, + unsigned long *end_pfn); +void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn, + unsigned long *out_end_pfn, int *out_nid); + +/** + * for_each_mem_pfn_range - early memory pfn range iterator + * @i: an integer used as loop variable + * @nid: node selector, %MAX_NUMNODES for all nodes + * @p_start: ptr to ulong for start pfn of the range, can be %NULL + * @p_end: ptr to ulong for end pfn of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + * + * Walks over configured memory ranges. + */ +#define for_each_mem_pfn_range(i, nid, p_start, p_end, p_nid) \ + for (i = -1, __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid); \ + i >= 0; __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid)) +#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ + +#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT +void __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone, + unsigned long *out_spfn, + unsigned long *out_epfn); +/** + * for_each_free_mem_range_in_zone - iterate through zone specific free + * memblock areas + * @i: u64 used as loop variable + * @zone: zone in which all of the memory blocks reside + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * + * Walks over free (memory && !reserved) areas of memblock in a specific + * zone. Available once memblock and an empty zone is initialized. The main + * assumption is that the zone start, end, and pgdat have been associated. + * This way we can use the zone to determine NUMA node, and if a given part + * of the memblock is valid for the zone. + */ +#define for_each_free_mem_pfn_range_in_zone(i, zone, p_start, p_end) \ + for (i = 0, \ + __next_mem_pfn_range_in_zone(&i, zone, p_start, p_end); \ + i != U64_MAX; \ + __next_mem_pfn_range_in_zone(&i, zone, p_start, p_end)) + +/** + * for_each_free_mem_range_in_zone_from - iterate through zone specific + * free memblock areas from a given point + * @i: u64 used as loop variable + * @zone: zone in which all of the memory blocks reside + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * + * Walks over free (memory && !reserved) areas of memblock in a specific + * zone, continuing from current position. Available as soon as memblock is + * initialized. + */ +#define for_each_free_mem_pfn_range_in_zone_from(i, zone, p_start, p_end) \ + for (; i != U64_MAX; \ + __next_mem_pfn_range_in_zone(&i, zone, p_start, p_end)) +#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ + +/** + * for_each_free_mem_range - iterate through free memblock areas + * @i: u64 used as loop variable + * @nid: node selector, %NUMA_NO_NODE for all nodes + * @flags: pick from blocks based on memory attributes + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + * + * Walks over free (memory && !reserved) areas of memblock. Available as + * soon as memblock is initialized. + */ +#define for_each_free_mem_range(i, nid, flags, p_start, p_end, p_nid) \ + for_each_mem_range(i, &memblock.memory, &memblock.reserved, \ + nid, flags, p_start, p_end, p_nid) + +/** + * for_each_free_mem_range_reverse - rev-iterate through free memblock areas + * @i: u64 used as loop variable + * @nid: node selector, %NUMA_NO_NODE for all nodes + * @flags: pick from blocks based on memory attributes + * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL + * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL + * @p_nid: ptr to int for nid of the range, can be %NULL + * + * Walks over free (memory && !reserved) areas of memblock in reverse + * order. Available as soon as memblock is initialized. + */ +#define for_each_free_mem_range_reverse(i, nid, flags, p_start, p_end, \ + p_nid) \ + for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, \ + nid, flags, p_start, p_end, p_nid) + +#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP +int memblock_set_node(phys_addr_t base, phys_addr_t size, + struct memblock_type *type, int nid); + +static inline void memblock_set_region_node(struct memblock_region *r, int nid) +{ + r->nid = nid; +} + +static inline int memblock_get_region_node(const struct memblock_region *r) +{ + return r->nid; +} +#else +static inline void memblock_set_region_node(struct memblock_region *r, int nid) +{ +} + +static inline int memblock_get_region_node(const struct memblock_region *r) +{ + return 0; +} +#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ + +/* Flags for memblock allocation APIs */ +#define MEMBLOCK_ALLOC_ANYWHERE (~(phys_addr_t)0) +#define MEMBLOCK_ALLOC_ACCESSIBLE 0 +#define MEMBLOCK_ALLOC_KASAN 1 + +/* We are using top down, so it is safe to use 0 here */ +#define MEMBLOCK_LOW_LIMIT 0 + +#ifndef ARCH_LOW_ADDRESS_LIMIT +#define ARCH_LOW_ADDRESS_LIMIT 0xffffffffUL +#endif + +phys_addr_t memblock_phys_alloc_range(phys_addr_t size, phys_addr_t align, + phys_addr_t start, phys_addr_t end); +phys_addr_t memblock_phys_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid); + +static inline phys_addr_t memblock_phys_alloc(phys_addr_t size, + phys_addr_t align) +{ + return memblock_phys_alloc_range(size, align, 0, + MEMBLOCK_ALLOC_ACCESSIBLE); +} + +void *memblock_alloc_exact_nid_raw(phys_addr_t size, phys_addr_t align, + phys_addr_t min_addr, phys_addr_t max_addr, + int nid); +void *memblock_alloc_try_nid_raw(phys_addr_t size, phys_addr_t align, + phys_addr_t min_addr, phys_addr_t max_addr, + int nid); +void *memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, + phys_addr_t min_addr, phys_addr_t max_addr, + int nid); + +static inline void * __init memblock_alloc(phys_addr_t size, phys_addr_t align) +{ + return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, + MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE); +} + +static inline void * __init memblock_alloc_raw(phys_addr_t size, + phys_addr_t align) +{ + return memblock_alloc_try_nid_raw(size, align, MEMBLOCK_LOW_LIMIT, + MEMBLOCK_ALLOC_ACCESSIBLE, + NUMA_NO_NODE); +} + +static inline void * __init memblock_alloc_from(phys_addr_t size, + phys_addr_t align, + phys_addr_t min_addr) +{ + return memblock_alloc_try_nid(size, align, min_addr, + MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE); +} + +static inline void * __init memblock_alloc_low(phys_addr_t size, + phys_addr_t align) +{ + return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, + ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE); +} + +static inline void * __init memblock_alloc_node(phys_addr_t size, + phys_addr_t align, int nid) +{ + return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, + MEMBLOCK_ALLOC_ACCESSIBLE, nid); +} + +static inline void __init memblock_free_early(phys_addr_t base, + phys_addr_t size) +{ + memblock_free(base, size); +} + +static inline void __init memblock_free_early_nid(phys_addr_t base, + phys_addr_t size, int nid) +{ + memblock_free(base, size); +} + +static inline void __init memblock_free_late(phys_addr_t base, phys_addr_t size) +{ + __memblock_free_late(base, size); +} + +/* + * Set the allocation direction to bottom-up or top-down. + */ +static inline void __init memblock_set_bottom_up(bool enable) +{ + memblock.bottom_up = enable; +} + +/* + * Check if the allocation direction is bottom-up or not. + * if this is true, that said, memblock will allocate memory + * in bottom-up direction. + */ +static inline bool memblock_bottom_up(void) +{ + return memblock.bottom_up; +} + +phys_addr_t memblock_phys_mem_size(void); +phys_addr_t memblock_reserved_size(void); +phys_addr_t memblock_mem_size(unsigned long limit_pfn); +phys_addr_t memblock_start_of_DRAM(void); +phys_addr_t memblock_end_of_DRAM(void); +void memblock_enforce_memory_limit(phys_addr_t memory_limit); +void memblock_cap_memory_range(phys_addr_t base, phys_addr_t size); +void memblock_mem_limit_remove_map(phys_addr_t limit); +bool memblock_is_memory(phys_addr_t addr); +bool memblock_is_map_memory(phys_addr_t addr); +bool memblock_is_region_memory(phys_addr_t base, phys_addr_t size); +bool memblock_is_reserved(phys_addr_t addr); +bool memblock_is_region_reserved(phys_addr_t base, phys_addr_t size); + +extern void __memblock_dump_all(void); + +static inline void memblock_dump_all(void) +{ + if (memblock_debug) + __memblock_dump_all(); +} + +/** + * memblock_set_current_limit - Set the current allocation limit to allow + * limiting allocations to what is currently + * accessible during boot + * @limit: New limit value (physical address) + */ +void memblock_set_current_limit(phys_addr_t limit); + + +phys_addr_t memblock_get_current_limit(void); + +/* + * pfn conversion functions + * + * While the memory MEMBLOCKs should always be page aligned, the reserved + * MEMBLOCKs may not be. This accessor attempt to provide a very clear + * idea of what they return for such non aligned MEMBLOCKs. + */ + +/** + * memblock_region_memory_base_pfn - get the lowest pfn of the memory region + * @reg: memblock_region structure + * + * Return: the lowest pfn intersecting with the memory region + */ +static inline unsigned long memblock_region_memory_base_pfn(const struct memblock_region *reg) +{ + return PFN_UP(reg->base); +} + +/** + * memblock_region_memory_end_pfn - get the end pfn of the memory region + * @reg: memblock_region structure + * + * Return: the end_pfn of the reserved region + */ +static inline unsigned long memblock_region_memory_end_pfn(const struct memblock_region *reg) +{ + return PFN_DOWN(reg->base + reg->size); +} + +/** + * memblock_region_reserved_base_pfn - get the lowest pfn of the reserved region + * @reg: memblock_region structure + * + * Return: the lowest pfn intersecting with the reserved region + */ +static inline unsigned long memblock_region_reserved_base_pfn(const struct memblock_region *reg) +{ + return PFN_DOWN(reg->base); +} + +/** + * memblock_region_reserved_end_pfn - get the end pfn of the reserved region + * @reg: memblock_region structure + * + * Return: the end_pfn of the reserved region + */ +static inline unsigned long memblock_region_reserved_end_pfn(const struct memblock_region *reg) +{ + return PFN_UP(reg->base + reg->size); +} + +#define for_each_memblock(memblock_type, region) \ + for (region = memblock.memblock_type.regions; \ + region < (memblock.memblock_type.regions + memblock.memblock_type.cnt); \ + region++) + +#define for_each_memblock_type(i, memblock_type, rgn) \ + for (i = 0, rgn = &memblock_type->regions[0]; \ + i < memblock_type->cnt; \ + i++, rgn = &memblock_type->regions[i]) + +extern void *alloc_large_system_hash(const char *tablename, + unsigned long bucketsize, + unsigned long numentries, + int scale, + int flags, + unsigned int *_hash_shift, + unsigned int *_hash_mask, + unsigned long low_limit, + unsigned long high_limit); + +#define HASH_EARLY 0x00000001 /* Allocating during early boot? */ +#define HASH_SMALL 0x00000002 /* sub-page allocation allowed, min + * shift passed via *_hash_shift */ +#define HASH_ZERO 0x00000004 /* Zero allocated hash table */ + +/* Only NUMA needs hash distribution. 64bit NUMA architectures have + * sufficient vmalloc space. + */ +#ifdef CONFIG_NUMA +#define HASHDIST_DEFAULT IS_ENABLED(CONFIG_64BIT) +extern int hashdist; /* Distribute hashes across NUMA nodes? */ +#else +#define hashdist (0) +#endif + +#ifdef CONFIG_MEMTEST +extern void early_memtest(phys_addr_t start, phys_addr_t end); +#else +static inline void early_memtest(phys_addr_t start, phys_addr_t end) +{ +} +#endif + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_MEMBLOCK_H */ From 4ea9120101c1ae0f1dcb6aa2bbda2cc1a09cb651 Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 3 Oct 2020 21:21:59 -0400 Subject: [PATCH 203/529] Add test file for issue-134-specific tests New file t/tree/issue134.c exercises the bugs noted in . I did not copy in the files listed in that comment because those files are GPL-2.0-only or an unspecified GPL license, not GPL-2.0-or-later. --- t/tree/issue134.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 t/tree/issue134.c diff --git a/t/tree/issue134.c b/t/tree/issue134.c new file mode 100644 index 00000000..98c6fff1 --- /dev/null +++ b/t/tree/issue134.c @@ -0,0 +1,49 @@ +/* + * issue134.c: tests exhibiting problems noted in + * https://github.com/bootlin/elixir/issues/134#issue-624392766 + * + * t/tree/issue134.c + * SPDX-License-Identifier: CC0-1.0 + */ + +/** + * issue134_function1: + * Do something magical + * and something quotidian + * + * @p1: Param + * + * Do something + * + * Returns whatever + */ +int issue134_function1(int p1) +{ + write_test_cases(); +} + +/** + * issue134_function2: - does what it does + * @param: something + * + * Whatever + * + * Whatever, paragraph 2. + */ +void __sched issue134_function2(struct completion *x) +{ + take_action(); +} + +/** + * issue134_function3() - something + * @param: whatever + * @paramii: whatever else + * + * Description + * + * Return: %magic or + * %-EIEIO if the farm bought + */ +int issue134_function3(struct issue134_struct1 *param, + struct issue134_struct2 *paramii); From 224ec06ced7752a08bcac45f3810ded95617725b Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 3 Oct 2020 09:34:46 -0400 Subject: [PATCH 204/529] Add tests for struct, enum, macro doc comments --- t/300-doc-comments.t | 87 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index 0e8a5baa..f2b3f414 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -76,4 +76,91 @@ run_produces_ok('ident query (existent, function, documented in C file, #102)', ], MUST_SUCCEED); +# Non-functions + +run_produces_ok('ident query (existent, enum, documented in H file)', + [$tenv->query_py, qw(v5.4 ident memblock_flags C)], + [ + qr{^Documented in:}, + {doc => qr{\bmemblock\.h.+\b28\b}}, + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, enum, not documented)', + [$tenv->query_py, qw(v5.4 ident rseq_cpu_id_state C)], # uapi/linux/rseq.h:16 + [ + qr{^Documented in:}, + {doc => { not => qr{/} }} + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, struct, documented in H file)', + [$tenv->query_py, qw(v5.4 ident memblock_region C)], + [ + qr{^Documented in:}, + {doc => qr{\bmemblock\.h.+\b42\b}}, + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, struct, not documented)', + [$tenv->query_py, qw(v5.4 ident epoll_event C)], # eventpoll.h:77 + [ + qr{^Documented in:}, + {doc => { not => qr{/} }} + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, macro, documented in H file)', + [$tenv->query_py, qw(v5.4 ident for_each_mem_range C)], + [ + qr{^Documented in:}, + {doc => qr{\bmemblock\.h.+\b148\b}}, + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, macro, not documented)', + [$tenv->query_py, qw(v5.4 ident MEMBLOCK_LOW_LIMIT C)], # memblock.h:343 + [ + qr{^Documented in:}, + {doc => { not => qr{/} }} + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, macro, not documented)', + [$tenv->query_py, qw(v5.4 ident MEMBLOCK_LOW_LIMIT C)], # memblock.h:343 + [ + qr{^Documented in:}, + {doc => { not => qr{/} }} + ], + MUST_SUCCEED); + +# Specific cases from #134 + +# Like regmap_update_bits_base() +run_produces_ok('ident query (existent, function, documented in C file, nonstandard doc comment, #134)', + [$tenv->query_py, qw(v5.4 ident issue134_function1 C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue134\.c.+\b9\b}}, + ], + MUST_SUCCEED); + +# Like wait_for_completion() +run_produces_ok('ident query (existent, function, documented in C file, nonstandard doc comment, #134)', + [$tenv->query_py, qw(v5.4 ident issue134_function2 C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue134\.c.+\b25\b}}, + ], + MUST_SUCCEED); + +# Like v4l2_fwnode_endpoint_parse() +run_produces_ok('ident query (existent, prototype, documented in C file), #134', + [$tenv->query_py, qw(v5.4 ident issue134_function3 C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue134\.c.+\b38\b}}, + ], + MUST_SUCCEED); + done_testing; From 5ba86bde4d6bf31a2a33b0caf6d747d272b46b8d Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 3 Oct 2020 21:46:51 -0400 Subject: [PATCH 205/529] Improve doc-comment extraction (#134) - Extract struct, enum, and macro docs - Permit more forms of comment header. Accept some that are technically ill-formed per [1] --- I think it's better for end-users if we are a bit more forgiving. [1] --- find-file-doc-comments.pl | 53 ++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl index 99994d91..19c7ab3b 100755 --- a/find-file-doc-comments.pl +++ b/find-file-doc-comments.pl @@ -45,10 +45,11 @@ sub main { my @ctags_parsed = map { [split] } @ctags; # Flip it around to index functions by line - my %function_lines; + my %definition_lines; + my %definition_types; for my $tag (@ctags_parsed) { - next unless $tag->[1] eq 'function'; - $function_lines{$tag->[2]} = $tag->[0]; + $definition_lines{$tag->[2]} = $tag->[0]; + $definition_types{$tag->[0]} = $tag->[1] // ''; } if($VERBOSE) { @@ -66,26 +67,44 @@ sub main { # Work backwards through the file and look for doc comments my %doc_comments; - my $doc_comment_opener = qr{^\s*\/\*\*(?:\s|$)}; # Start of doc comment + my $doc_comment_opener = qr{^\h*\/\*\*(?:\h|$)}; # Start of doc comment + my $comment_leader = qr{\h+\*\h+(?:(?:struct|enum|union|typedef)\h+)?}; for(my $lineno = $#source_lines ; $lineno >= 1 ; --$lineno) { - next unless exists $function_lines{$lineno}; - my $func_name = $function_lines{$lineno}; - print "Checking for $func_name @ $lineno\n" if $VERBOSE; + next unless exists $definition_lines{$lineno}; + my $definition_name = $definition_lines{$lineno}; + print "Checking $definition_name @ $lineno\n" if $VERBOSE; + # Comment header: be liberal in what we accept. For example, do not + # check the type of the definition/declaration against the type in + # the comment header. I don't think this will be a problem. my $this_doc_comment_header = - qr{^\s+\*\s+\Q$func_name\E(?:\s|\(|$)}; + qr{^$comment_leader\Q$definition_name\E(?:\h|\(|:|$)}; print " Regex is -$this_doc_comment_header-\n" if $VERBOSE; + + if($definition_types{$definition_name} eq 'macro') { + # Make sure we get back past the first line of a multiline macro + --$lineno while $lineno && $source_lines[$lineno] !~ /^#\h*define/; + } + + # Assume cflags gave us the first line of the definition, or we got + # back to it manually. Move to the first line that might be a doc comment. --$lineno; - print " Line $lineno is: $source_lines[$lineno]\n" if $VERBOSE; + + # TODO make sure we're not still in the definition. + # E.g., memblock.h:for_each_mem_range(). The defintion is reported + # on the second line of the #define, not the first line. + + print " Starting search for docs at line $lineno\n" if $VERBOSE; + # Find the last line that could be a doc-comment header # for this function. while($lineno && $source_lines[$lineno] =~ qr{ - ^\s*$ # Empty line - | ^\s+\*\/ # End of comment - | ^\s+\*(?:\s|$) # Continuation of comment + ^\h*$ # Empty line + | ^\h+\*\/ # End of comment + | ^\h+\*(?:\h|$) # Continuation of comment | $this_doc_comment_header }x) { print "$source_lines[$lineno] passed\n" if $VERBOSE; @@ -95,7 +114,7 @@ sub main { # because we may have just skipped past $this_doc_comment_header # Is it actually a header for this function? - print "Checking $source_lines[$lineno] for header\n" if $VERBOSE; + print "Checking line $lineno for header\n" if $VERBOSE; next unless $source_lines[$lineno] =~ $this_doc_comment_header; # We have found a header. Confirm it's a doc comment. @@ -104,13 +123,13 @@ sub main { print " * Match\n" if $VERBOSE; # We have found a doc comment for this function! Record it. - push @{$doc_comments{$func_name}}, $lineno; + push @{$doc_comments{$definition_name}}, $lineno; } # Report the doc comments for each function - for my $funcname (keys %doc_comments) { - my $comment_lines = $doc_comments{$funcname}; - print "$funcname $_\n" foreach @$comment_lines; + for my $definition_name (keys %doc_comments) { + my $comment_lines = $doc_comments{$definition_name}; + print "$definition_name $_\n" foreach @$comment_lines; } return 0; From 5aeb22b3e7eb6ae33dae45fc5fe7bc37bb90193e Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 10 Oct 2020 17:03:15 -0400 Subject: [PATCH 206/529] Fix typo in TestEnvironment docs [minor] --- t/TestEnvironment.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/TestEnvironment.pm b/t/TestEnvironment.pm index 82376712..18a54daa 100644 --- a/t/TestEnvironment.pm +++ b/t/TestEnvironment.pm @@ -53,7 +53,7 @@ use constant PROJECT => 'testproj'; =head2 lxr_proj_dir -C<$lxr_proj_dir> is the value to use in the C environment +C<$lxr_proj_dir> is the value to use in the C environment variable. =head2 lxr_data_dir From 68232f8fba68cd17be239d65084ac2603fa08c14 Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 10 Oct 2020 16:44:42 -0400 Subject: [PATCH 207/529] Added test file and test for #186 --- t/300-doc-comments.t | 7 +++++++ t/tree/issue186.c | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 t/tree/issue186.c diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index f2b3f414..eec22d81 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -27,6 +27,7 @@ use autodie; # note: still need to check system() calls manually use FindBin '$Bin'; use lib $Bin; +use File::Spec; use Test::More; use TestEnvironment; @@ -163,4 +164,10 @@ run_produces_ok('ident query (existent, prototype, documented in C file), #134', ], MUST_SUCCEED); +# #186 +run_produces_ok('No warnings on macro, #186', + [$tenv->find_doc, File::Spec->catfile($tenv->lxr_repo_dir, 'issue186.c')], + [ ], + MUST_SUCCEED); # warnings appear on stderr, failing the MUST_SUCCEED checks + done_testing; diff --git a/t/tree/issue186.c b/t/tree/issue186.c new file mode 100644 index 00000000..8cc01b3a --- /dev/null +++ b/t/tree/issue186.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: CC0-1.0 +// This file triggers the bug described in #186. + +#ifdef SOMETHING +static int undocumented_function(struct platform_device *pdev) +{ +} + +#else +#define undocumented_function NULL +#endif From 81e0c296e4a8db13d9679d6c755fd007d5a30dbf Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 10 Oct 2020 17:04:18 -0400 Subject: [PATCH 208/529] Extract doc with multiple types for an identifier Fixes #186. In find-file-doc-comments.pl: - Index doc comments by line, not identifier, since an identifier may be defined differently on different lines. - Clean up debug printing so that the verbose output is easier to read. - Use say() instead of print() to save space (say() automatically adds the `\n` to the output line). --- find-file-doc-comments.pl | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl index 19c7ab3b..9fa7ab81 100755 --- a/find-file-doc-comments.pl +++ b/find-file-doc-comments.pl @@ -38,22 +38,23 @@ sub main { grep -av "^operator " | awk '{print \$1" "\$2" "\$3}' }; die "Could not get ctags: $!" if $!; - print "No ctags results" if $VERBOSE && !@ctags; + say "No ctags results" if $VERBOSE && !@ctags; return 0 unless @ctags; # Make a list of [name, type, line] arrays my @ctags_parsed = map { [split] } @ctags; - # Flip it around to index functions by line + # Flip it around to index functions and types by line. Don't index anything + # by name, since there can be multiple names with different types/lines (#186). my %definition_lines; my %definition_types; for my $tag (@ctags_parsed) { $definition_lines{$tag->[2]} = $tag->[0]; - $definition_types{$tag->[0]} = $tag->[1] // ''; + $definition_types{$tag->[2]} = $tag->[1] // ''; } if($VERBOSE) { - for my $tag (@ctags_parsed) { + for my $tag (sort { $a->[2] <=> $b->[2] } @ctags_parsed) { say $tag->[2], ': ', $tag->[0], ' is a(n) ', $tag->[1]; } } @@ -73,16 +74,17 @@ sub main { for(my $lineno = $#source_lines ; $lineno >= 1 ; --$lineno) { next unless exists $definition_lines{$lineno}; my $definition_name = $definition_lines{$lineno}; - print "Checking $definition_name @ $lineno\n" if $VERBOSE; + my $definition_type = $definition_types{$lineno}; + say "\nChecking $definition_type $definition_name @ $lineno" if $VERBOSE; # Comment header: be liberal in what we accept. For example, do not # check the type of the definition/declaration against the type in # the comment header. I don't think this will be a problem. my $this_doc_comment_header = qr{^$comment_leader\Q$definition_name\E(?:\h|\(|:|$)}; - print " Regex is -$this_doc_comment_header-\n" if $VERBOSE; + say " Regex is -$this_doc_comment_header-" if $VERBOSE; - if($definition_types{$definition_name} eq 'macro') { + if($definition_type eq 'macro') { # Make sure we get back past the first line of a multiline macro --$lineno while $lineno && $source_lines[$lineno] !~ /^#\h*define/; } @@ -95,8 +97,7 @@ sub main { # E.g., memblock.h:for_each_mem_range(). The defintion is reported # on the second line of the #define, not the first line. - print " Starting search for docs at line $lineno\n" if $VERBOSE; - + say " Starting search for docs at line $lineno" if $VERBOSE; # Find the last line that could be a doc-comment header # for this function. @@ -107,20 +108,24 @@ sub main { | ^\h+\*(?:\h|$) # Continuation of comment | $this_doc_comment_header }x) { - print "$source_lines[$lineno] passed\n" if $VERBOSE; + if($VERBOSE) { + my $line = $source_lines[$lineno]; + chomp $line; + say " skipped $lineno '$line'"; + } --$lineno; } ++$lineno; # Check the last line that matched, # because we may have just skipped past $this_doc_comment_header # Is it actually a header for this function? - print "Checking line $lineno for header\n" if $VERBOSE; + say " Checking line $lineno for header" if $VERBOSE; next unless $source_lines[$lineno] =~ $this_doc_comment_header; # We have found a header. Confirm it's a doc comment. --$lineno; next unless $lineno > 0 && $source_lines[$lineno] =~ $doc_comment_opener; - print " * Match\n" if $VERBOSE; + say " * Match" if $VERBOSE; # We have found a doc comment for this function! Record it. push @{$doc_comments{$definition_name}}, $lineno; @@ -129,7 +134,7 @@ sub main { # Report the doc comments for each function for my $definition_name (keys %doc_comments) { my $comment_lines = $doc_comments{$definition_name}; - print "$definition_name $_\n" foreach @$comment_lines; + say "$definition_name $_" foreach @$comment_lines; } return 0; From 90392b543d94869bd65c3e35e8d1cedb742590af Mon Sep 17 00:00:00 2001 From: Chris White Date: Sat, 10 Oct 2020 17:22:26 -0400 Subject: [PATCH 209/529] doc-comment tests: add counterexamples for issue186 Issue 186 relates to symbols that are not documented. Add tests for symbols that are documented in a file of the same structure. --- t/300-doc-comments.t | 19 +++++++++++++++++++ t/tree/issue186-counterexamples.c | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 t/tree/issue186-counterexamples.c diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index eec22d81..30326c3c 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -170,4 +170,23 @@ run_produces_ok('No warnings on macro, #186', [ ], MUST_SUCCEED); # warnings appear on stderr, failing the MUST_SUCCEED checks +# #186 counterexamples +run_produces_ok('ident query (existent, documented as function), #186 counterexample', + [$tenv->query_py, qw(v5.4 ident i186c_fn1 C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue186-counterexamples\.c.+\b5\b}}, + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, documented as macro), #186 counterexample', + [$tenv->query_py, qw(v5.4 ident i186c_fn2 C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue186-counterexamples\.c.+\b20\b}}, + ], + MUST_SUCCEED); + +######################################################################### + done_testing; diff --git a/t/tree/issue186-counterexamples.c b/t/tree/issue186-counterexamples.c new file mode 100644 index 00000000..379f562d --- /dev/null +++ b/t/tree/issue186-counterexamples.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: CC0-1.0 +// This file is the situation of #186, but with documentation. + +#ifdef SOMETHING +/** + * i186c_fn1 - do something + */ +static int i186c_fn1(struct platform_device *pdev) +{ +} +#else +#define i186c_fn1 NULL +#endif + +#ifdef SOMETHING +static int i186c_fn2(struct platform_device *pdev) +{ +} +#else +/** + * i186c_fn2 - do something + */ +#define i186c_fn2 NULL +#endif From ff6a55bb5775d82b16eeae0d676dd9976b4a1ddf Mon Sep 17 00:00:00 2001 From: Chris White Date: Thu, 15 Oct 2020 20:31:24 -0400 Subject: [PATCH 210/529] Added test of #188 --- t/300-doc-comments.t | 6 ++++++ t/tree/issue188.c | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 t/tree/issue188.c diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index 30326c3c..efe09121 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -187,6 +187,12 @@ run_produces_ok('ident query (existent, documented as macro), #186 counterexampl ], MUST_SUCCEED); +# #188 +run_produces_ok('No warnings on indented #define, #188', + [$tenv->find_doc, File::Spec->catfile($tenv->lxr_repo_dir, 'issue188.c')], + [ ], + MUST_SUCCEED); # warnings appear on stderr, failing the MUST_SUCCEED checks + ######################################################################### done_testing; diff --git a/t/tree/issue188.c b/t/tree/issue188.c new file mode 100644 index 00000000..1f6c3003 --- /dev/null +++ b/t/tree/issue188.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: CC0-1.0 +// This file triggers the bug described in #188. + +static int undocumented_function(struct platform_device *pdev) +{ + int foo; + #define BAR (42) + foo=1; + return foo; +} From b74017c9df0fbd6078a0bb3a86bef75cfd10b739 Mon Sep 17 00:00:00 2001 From: Chris White Date: Thu, 15 Oct 2020 20:56:48 -0400 Subject: [PATCH 211/529] doc comments: support indented #defines Fixes #188. --- find-file-doc-comments.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl index 9fa7ab81..ea315ef8 100755 --- a/find-file-doc-comments.pl +++ b/find-file-doc-comments.pl @@ -86,7 +86,7 @@ sub main { if($definition_type eq 'macro') { # Make sure we get back past the first line of a multiline macro - --$lineno while $lineno && $source_lines[$lineno] !~ /^#\h*define/; + --$lineno while $lineno && $source_lines[$lineno] !~ /^\h*#\h*define/; } # Assume cflags gave us the first line of the definition, or we got From 65c5db26b81bdccaa56ab447228c738c1a6cf175 Mon Sep 17 00:00:00 2001 From: Chris White Date: Thu, 15 Oct 2020 21:10:26 -0400 Subject: [PATCH 212/529] find-file-doc-comments: report filename on warning Whenever the program issues a warning, report which filename was being processed. This should make it easier to diagnose any future warnings (e.g., #186, #188). --- find-file-doc-comments.pl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl index ea315ef8..cb6e9a0e 100755 --- a/find-file-doc-comments.pl +++ b/find-file-doc-comments.pl @@ -22,7 +22,7 @@ use 5.010001; use strict; -use warnings FATAL => qw(uninitialized); +use warnings; use autodie; my $VERBOSE = $ENV{V}; @@ -31,10 +31,16 @@ sub main { die "Need a filename" unless @_; - say "Processing file $_[0]" if $VERBOSE; + my $filename = shift; + say "Processing file $filename" if $VERBOSE; + + # Fatalize all warnings, and log which file triggered the warning. + local $SIG{__WARN__} = sub { + die "While processing $filename: $_[0]\n"; + }; # Do `script.sh parse-defs` on the file - my @ctags = qx{ ctags -x --c-kinds=+p-m --language-force=C "$_[0]" | + my @ctags = qx{ ctags -x --c-kinds=+p-m --language-force=C "$filename" | grep -av "^operator " | awk '{print \$1" "\$2" "\$3}' }; die "Could not get ctags: $!" if $!; @@ -60,7 +66,7 @@ sub main { } # Read the source file - open my $fh, '<', $_[0]; + open my $fh, '<', $filename; my @source_lines = (undef, <$fh>); # undef => indices in @source_lines match ctags's 1-based linenos close $fh; From 3da2d7f89c61f94e5d8420ed6582155d8249655e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 20 Oct 2020 21:23:32 +0200 Subject: [PATCH 213/529] http: drop unused imports --- http/autocomplete.py | 1 - 1 file changed, 1 deletion(-) diff --git a/http/autocomplete.py b/http/autocomplete.py index 8589ddc5..46be641f 100755 --- a/http/autocomplete.py +++ b/http/autocomplete.py @@ -19,7 +19,6 @@ # along with Elixir. If not, see . import cgi -import cgitb from urllib import parse import sys import os From cb0596029fef7d3c7bf584a6efb0e61d263fd3be Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 20 Oct 2020 21:28:27 +0200 Subject: [PATCH 214/529] data: remove unused import --- data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/data.py b/data.py index cbef5571..ef68d69c 100755 --- a/data.py +++ b/data.py @@ -19,7 +19,6 @@ # along with Elixir. If not, see . import bsddb3 -from io import BytesIO import re from lib import autoBytes import os From 016d848b38e1a2d421afd256939217da6ac79598 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 20 Oct 2020 21:29:16 +0200 Subject: [PATCH 215/529] update: remove unused imports --- update.py | 1 - 1 file changed, 1 deletion(-) diff --git a/update.py b/update.py index ed297917..72a37c51 100755 --- a/update.py +++ b/update.py @@ -23,7 +23,6 @@ # This is different from that blob's Git hash. from sys import argv -import os from threading import Thread, Lock, Event, Condition import lib From 681e6ca31f332901356107e1d94a5820365c94c5 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 20 Oct 2020 21:32:12 +0200 Subject: [PATCH 216/529] update: silence assigned but never use warnings As spotted by pyflakes --- update.py | 1 - 1 file changed, 1 deletion(-) diff --git a/update.py b/update.py index 72a37c51..088ba9e6 100755 --- a/update.py +++ b/update.py @@ -503,7 +503,6 @@ def update_compatibles_bindings(self, idxes): with hash_file_lock: hash = db.hash.get(idx) - filename = db.file.get(idx) family = 'B' lines = compatibles_parser.run(scriptLines('get-blob', hash), family) From db45f2f9dec69f3aa0012b2bc3ed2cd658fe9f5e Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Sat, 24 Oct 2020 12:10:44 +0200 Subject: [PATCH 217/529] docker: Execute update.py with unbuffered output This allows to have real time logging while building the database. Signed-off-by: Maxime Chretien (MixLeNain) --- docker/centos/Dockerfile | 2 +- docker/debian/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile index 2ab56f29..a22276aa 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos/Dockerfile @@ -65,7 +65,7 @@ ENV LXR_DATA_DIR /srv/elixir-data/$PROJECT/data RUN \ cd /usr/local/elixir/ && \ ./script.sh list-tags && \ - ./update.py + python3 -u ./update.py # apache elixir config, see elixir README # apache HttpProtolOptions workaround diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 163ceaec..f1adff56 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -57,7 +57,7 @@ ENV LXR_DATA_DIR /srv/elixir-data/$PROJECT/data RUN \ cd /usr/local/elixir/ && \ ./script.sh list-tags && \ - ./update.py + python3 -u ./update.py # apache elixir config, see elixir README # make apache less stricter about cgitb spam headers From f6bcfabcdc7f4ff6f350b1b298fa69eb1ecd9140 Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Mon, 26 Oct 2020 10:05:02 +0100 Subject: [PATCH 218/529] query: Sort definitions by type This sorts the definition buffer by file path and then by type (reversed) Sorting by type in reverse order allows to have structs definitions before members. Signed-off-by: Maxime Chretien (MixLeNain) --- query.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/query.py b/query.py index e3db127c..ae250253 100755 --- a/query.py +++ b/query.py @@ -353,8 +353,10 @@ def get_idents_defs(version, ident, family): if doc_idx == file_idx: # TODO should this be a `while`? docBuf.append((file_path, doc_line)) + # Sort dBuf by path name before sorting by type in the loop + dBuf.sort() - for path, type, dline in sorted(dBuf): + for path, type, dline in sorted(dBuf, key=lambda d: d[1], reverse=True): symbol_definitions.append(SymbolInstance(path, dline, type)) for path, rlines in sorted(rBuf): From bec8105305f7946e92fcc9540195c249870fd0aa Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Mon, 26 Oct 2020 13:31:40 +0100 Subject: [PATCH 219/529] t/api_test.py : Update tests results according to query changes Signed-off-by: Maxime Chretien (MixLeNain) --- t/api_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/api_test.py b/t/api_test.py index 858f984b..5b052681 100644 --- a/t/api_test.py +++ b/t/api_test.py @@ -57,9 +57,9 @@ def test_existing_identifier(self): expected_json = { 'definitions': [ + {'path': 'include/linux/i2c.h', 'line': 941, 'type': 'prototype'}, {'path': 'drivers/i2c/i2c-core-of.c', 'line': 22, 'type': 'function'}, - {'path': 'include/linux/i2c.h', 'line': 968, 'type': 'function'}, - {'path': 'include/linux/i2c.h', 'line': 941, 'type': 'prototype'} + {'path': 'include/linux/i2c.h', 'line': 968, 'type': 'function'} ], 'references': [ From 868b8676b812fd6b1b90f34986fa7692b9d1ba56 Mon Sep 17 00:00:00 2001 From: Chris White Date: Mon, 26 Oct 2020 21:43:11 -0400 Subject: [PATCH 220/529] Add test of #192 --- t/300-doc-comments.t | 19 +++++++++++++++++++ t/tree/issue192.c | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 t/tree/issue192.c diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index efe09121..c2bd03d0 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -193,6 +193,25 @@ run_produces_ok('No warnings on indented #define, #188', [ ], MUST_SUCCEED); # warnings appear on stderr, failing the MUST_SUCCEED checks +# #192 + +# Like request_firmware() +run_produces_ok('ident query (existent, function, documented in C file, type on preceding line, #192)', + [$tenv->query_py, qw(v5.4 ident issue192a C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue192\.c.+\b5\b}}, + ], + MUST_SUCCEED); + +run_produces_ok('ident query (existent, function, documented in C file, uppercase return type on preceding line, #192)', + [$tenv->query_py, qw(v5.4 ident issue192b C)], + [ + qr{^Documented in:}, + {doc => qr{\bissue192\.c.+\b15\b}}, + ], + MUST_SUCCEED); + ######################################################################### done_testing; diff --git a/t/tree/issue192.c b/t/tree/issue192.c new file mode 100644 index 00000000..aa811be1 --- /dev/null +++ b/t/tree/issue192.c @@ -0,0 +1,24 @@ +/* t/tree/issue192.c */ +/* SPDX-License-Identifier: CC0-1.0 */ +/* This file triggers the bug described in #192. */ + +/** + * issue192a: - do something + **/ +int +issue192a(const struct foo **bar, const char *bat, + struct baz *quux) +{ + return 0; +} + +/** + * issue192b() + * + * Allow an uppercase return type + */ +AMAZING_RETURN_TYPE +issue192b(void *parameter) +{ + return (AMAZING_RETURN_TYPE)31337; +} From e544e25ee5574ffa6af583653aad4d3b4035b410 Mon Sep 17 00:00:00 2001 From: Chris White Date: Mon, 26 Oct 2020 21:48:39 -0400 Subject: [PATCH 221/529] find-file-doc-comments: handle "type\nname" Previously, find-file-doc-comments could handle int foo() {} but not int foo() {} When processing a function, if the reported line begins with the function's symbol, assume we have the latter situation. Move backward to the first line that doesn't start with an ASCII letter. This is my best current guess as to a reasonable heuristic. --- find-file-doc-comments.pl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/find-file-doc-comments.pl b/find-file-doc-comments.pl index cb6e9a0e..e14028f3 100755 --- a/find-file-doc-comments.pl +++ b/find-file-doc-comments.pl @@ -90,9 +90,14 @@ sub main { qr{^$comment_leader\Q$definition_name\E(?:\h|\(|:|$)}; say " Regex is -$this_doc_comment_header-" if $VERBOSE; + # Make sure we get back past the first line of multiline definitions if($definition_type eq 'macro') { - # Make sure we get back past the first line of a multiline macro --$lineno while $lineno && $source_lines[$lineno] !~ /^\h*#\h*define/; + } elsif($definition_type eq 'function') { + # Try to handle the case of "int\nfoo()" + if($source_lines[$lineno] =~ /^\h*\Q$definition_name\E\b/) { + --$lineno while $lineno && $source_lines[$lineno] =~ /^[a-z_]/i; + } } # Assume cflags gave us the first line of the definition, or we got @@ -135,7 +140,7 @@ sub main { # We have found a doc comment for this function! Record it. push @{$doc_comments{$definition_name}}, $lineno; - } + } # foreach line in reverse order # Report the doc comments for each function for my $definition_name (keys %doc_comments) { @@ -144,4 +149,4 @@ sub main { } return 0; -} +} #main From 7613d5be6113d122f4d71963b6afdb4df376a44d Mon Sep 17 00:00:00 2001 From: Chris White Date: Mon, 26 Oct 2020 21:45:20 -0400 Subject: [PATCH 222/529] Remove duplicate test [minor] Apparently a copy-and-paste error --- we were running one test twice. Remove the second test, since I can't think of any reason we would need it. --- t/300-doc-comments.t | 8 -------- 1 file changed, 8 deletions(-) diff --git a/t/300-doc-comments.t b/t/300-doc-comments.t index c2bd03d0..281e50bc 100644 --- a/t/300-doc-comments.t +++ b/t/300-doc-comments.t @@ -127,14 +127,6 @@ run_produces_ok('ident query (existent, macro, not documented)', ], MUST_SUCCEED); -run_produces_ok('ident query (existent, macro, not documented)', - [$tenv->query_py, qw(v5.4 ident MEMBLOCK_LOW_LIMIT C)], # memblock.h:343 - [ - qr{^Documented in:}, - {doc => { not => qr{/} }} - ], - MUST_SUCCEED); - # Specific cases from #134 # Like regmap_update_bits_base() From f867ebfb8ea5c936cabf96470268eb700645fe5f Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Fri, 30 Oct 2020 10:40:05 +0100 Subject: [PATCH 223/529] web.py : Split defintion list into lists for each type This splits definition printing into multiple lists, one for each type. It allows to find wanted definitions faster when a single name is used for multiple definitions. Signed-off-by: Maxime Chretien (MixLeNain) --- http/web.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/http/web.py b/http/web.py index eda59e28..bd175e7b 100755 --- a/http/web.py +++ b/http/web.py @@ -336,9 +336,24 @@ def print(arg, end='\n'): print('
') if len(symbol_definitions) or len(symbol_references): if len(symbol_definitions): - print('

Defined in '+str(len(symbol_definitions))+' files:

') - print('
    ') + previous_type = '' + types_count = {} + + # Count occurences of each type before printing for symbol_definition in symbol_definitions: + if (symbol_definition.type in types_count): + types_count[symbol_definition.type] += 1 + else: + types_count[symbol_definition.type] = 1 + + for symbol_definition in symbol_definitions: + if (symbol_definition.type != previous_type) : + if (previous_type != ''): + print('
') + print('

Defined in '+str(types_count[symbol_definition.type])+' files as a '+symbol_definition.type+':

') + print('
    ') + previous_type = symbol_definition.type + ln = str(symbol_definition.line).split(',') if len(ln) == 1: n = ln[0] From 9ec7cf5b2e697238cbd16d913d658b1e7be54d95 Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Fri, 30 Oct 2020 13:13:44 +0100 Subject: [PATCH 224/529] docker : Add symlink for pytest and dummy git config Also add perl test packages to centos Signed-off-by: Maxime Chretien (MixLeNain) --- docker/centos/Dockerfile | 10 +++++++++- docker/debian/Dockerfile | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile index a22276aa..92979b8e 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos/Dockerfile @@ -36,9 +36,13 @@ RUN \ jansson \ libyaml \ git \ - wget + wget \ + perl-Test-Most # httpd & perl is inherited from upstream openshift image +RUN \ + ln -s /usr/bin/pytest-3 /usr/bin/pytest + RUN \ wget https://bootlin.com/pub/elixir/universal-ctags-0+git~20e934e3-1.6.x86_64.rpm @@ -51,6 +55,10 @@ RUN \ RUN \ pip3 install Pygments-2.6.1.elixir-py3-none-any.whl +RUN \ + git config --global user.email 'elixir@dummy.com' && \ + git config --global user.name 'elixir' + RUN \ git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index f1adff56..74dc3445 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -28,6 +28,9 @@ RUN \ libyaml-0-2 \ wget +RUN \ + ln -s /usr/bin/pytest-3 /usr/bin/pytest + RUN \ wget https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb @@ -40,6 +43,10 @@ RUN \ RUN \ pip3 install Pygments-2.6.1.elixir-py3-none-any.whl +RUN \ + git config --global user.email 'elixir@dummy.com' && \ + git config --global user.name 'elixir' + RUN \ git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ From ac8b65a36d2ed6221aad683be065f9719aac8d41 Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Fri, 30 Oct 2020 15:58:04 +0100 Subject: [PATCH 225/529] docker: use rh-git218 package instead of git in centos This allows to have git 2.18 instead of git 1.8 and is needed for tests Signed-off-by: Maxime Chretien (MixLeNain) --- docker/centos/Dockerfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile index 92979b8e..92842168 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos/Dockerfile @@ -35,7 +35,7 @@ RUN \ perl-autodie \ jansson \ libyaml \ - git \ + rh-git218 \ wget \ perl-Test-Most # httpd & perl is inherited from upstream openshift image @@ -43,6 +43,11 @@ RUN \ RUN \ ln -s /usr/bin/pytest-3 /usr/bin/pytest +RUN \ + ln -s /opt/rh/rh-git218/root/usr/bin/* /usr/bin/ && \ + ln -s /opt/rh/rh-git218/root/usr/share/perl5/vendor_perl/* /usr/share/perl5/vendor_perl/ && \ + ln -s /opt/rh/httpd24/root/usr/lib64/lib* /usr/lib64/ + RUN \ wget https://bootlin.com/pub/elixir/universal-ctags-0+git~20e934e3-1.6.x86_64.rpm From 450b1d3a02d8dfb0cab9f9c0b742090e176f467c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 3 Nov 2020 14:51:16 +0100 Subject: [PATCH 226/529] Add optee support Fetch tags that look like a version number, i.e. "[0-9].". Signed-off-by: Michael Walle --- projects/optee.sh | 9 +++++++++ utils/index-all-repositories | 1 + utils/update-elixir-data | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 projects/optee.sh diff --git a/projects/optee.sh b/projects/optee.sh new file mode 100644 index 00000000..1075b62e --- /dev/null +++ b/projects/optee.sh @@ -0,0 +1,9 @@ +# Elixir definitions for OP-TEE Trusted OS + +list_tags_h() +{ + echo "$tags" | + grep '^[0-9]\.' | + tac | + sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 \1.\2 v\1.\2\3/' +} diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 63b5ac2c..68a8b73e 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -89,6 +89,7 @@ index llvm https://github.com/llvm/llvm-project.git & index mesa https://gitlab.freedesktop.org/mesa/mesa.git & index musl git://git.musl-libc.org/musl & index ofono https://git.kernel.org/pub/scm/network/ofono/ofono.git & +index optee https://github.com/OP-TEE/optee_os.git & index qemu https://git.qemu.org/git/qemu.git & index u-boot https://gitlab.denx.de/u-boot/u-boot.git & index uclibc-ng git://uclibc-ng.org/git/uclibc-ng & diff --git a/utils/update-elixir-data b/utils/update-elixir-data index 5306634b..32458311 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -30,7 +30,7 @@ if [ -z "$ELIXIR_INSTALL" ]; then exit 1 fi -for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono dpdk toybox grub; do +for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono optee dpdk toybox grub; do echo "Processing project $root/$p ..." export LXR_DATA_DIR=$root/$p/data export LXR_REPO_DIR=$root/$p/repo From fc517b659d8dfd07d28670481a9deae1b8ef8998 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 4 Nov 2020 09:00:46 +0100 Subject: [PATCH 227/529] Rename op-tee project Signed-off-by: Michael Opdenacker --- projects/{optee.sh => op-tee.sh} | 0 utils/index-all-repositories | 2 +- utils/update-elixir-data | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename projects/{optee.sh => op-tee.sh} (100%) diff --git a/projects/optee.sh b/projects/op-tee.sh similarity index 100% rename from projects/optee.sh rename to projects/op-tee.sh diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 68a8b73e..ccf1885a 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -89,7 +89,7 @@ index llvm https://github.com/llvm/llvm-project.git & index mesa https://gitlab.freedesktop.org/mesa/mesa.git & index musl git://git.musl-libc.org/musl & index ofono https://git.kernel.org/pub/scm/network/ofono/ofono.git & -index optee https://github.com/OP-TEE/optee_os.git & +index op-tee https://github.com/OP-TEE/optee_os.git & index qemu https://git.qemu.org/git/qemu.git & index u-boot https://gitlab.denx.de/u-boot/u-boot.git & index uclibc-ng git://uclibc-ng.org/git/uclibc-ng & diff --git a/utils/update-elixir-data b/utils/update-elixir-data index 32458311..92b222e9 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -30,7 +30,7 @@ if [ -z "$ELIXIR_INSTALL" ]; then exit 1 fi -for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono optee dpdk toybox grub; do +for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono op-tee dpdk toybox grub; do echo "Processing project $root/$p ..." export LXR_DATA_DIR=$root/$p/data export LXR_REPO_DIR=$root/$p/repo From 3b20f73281f4a5c0dc1fda64a49f2484f012d699 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 4 Nov 2020 09:08:17 +0100 Subject: [PATCH 228/529] op-tee: add tags filter Signed-off-by: Michael Opdenacker --- projects/op-tee.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/projects/op-tee.sh b/projects/op-tee.sh index 1075b62e..4047e50b 100644 --- a/projects/op-tee.sh +++ b/projects/op-tee.sh @@ -7,3 +7,9 @@ list_tags_h() tac | sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 \1.\2 v\1.\2\3/' } + +list_tags() +{ + echo "$tags" | + grep '^[0-9]\.' +} From 7992eb48c3ab125996062141bef424e91e889f4a Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 4 Nov 2020 09:15:44 +0100 Subject: [PATCH 229/529] Update definition for op-tee Signed-off-by: Michael Opdenacker --- projects/op-tee.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/projects/op-tee.sh b/projects/op-tee.sh index 4047e50b..f16a755e 100644 --- a/projects/op-tee.sh +++ b/projects/op-tee.sh @@ -13,3 +13,8 @@ list_tags() echo "$tags" | grep '^[0-9]\.' } + +get_latest() +{ + git tag | grep '^[0-9]\.' | grep -v '\-rc' | sort -V | tail -n 1 +} From 21fdc0c3ecfed7b302856a0f95007bfcab6697d7 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 4 Nov 2020 12:26:40 +0100 Subject: [PATCH 230/529] Fix tags for op-tee Signed-off-by: Michael Opdenacker --- projects/op-tee.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/op-tee.sh b/projects/op-tee.sh index f16a755e..d7c543d8 100644 --- a/projects/op-tee.sh +++ b/projects/op-tee.sh @@ -5,7 +5,7 @@ list_tags_h() echo "$tags" | grep '^[0-9]\.' | tac | - sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 \1.\2 v\1.\2\3/' + sed -r 's/^([0-9]*)\.([0-9]*)(.*)$/v\1 \1.\2 \1.\2\3/' } list_tags() From 73df02f07a36c7887cd6a24c47773078090df0cc Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Mon, 9 Nov 2020 11:59:00 +0100 Subject: [PATCH 231/529] docker: debian: Use pip3 version of falcon instead of debian package The debian package is outdated so tests fail. Signed-off-by: Maxime Chretien (MixLeNain) --- docker/debian/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 74dc3445..96756265 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -18,7 +18,6 @@ RUN \ python3-pip \ python3-jinja2 \ python3-bsddb3 \ - python3-falcon \ python3-pytest \ perl \ git \ @@ -28,6 +27,9 @@ RUN \ libyaml-0-2 \ wget +RUN \ + pip3 install falcon + RUN \ ln -s /usr/bin/pytest-3 /usr/bin/pytest From c7f427b672f079d6f4783d886fca70bb41c43814 Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Mon, 7 Dec 2020 11:36:51 +0100 Subject: [PATCH 232/529] update.py: Filter defs when indexing refs Signed-off-by: Maxime Chretien (MixLeNain) --- update.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/update.py b/update.py index 088ba9e6..31b724b1 100755 --- a/update.py +++ b/update.py @@ -54,6 +54,7 @@ new_idxes = [] # (new idxes, Event idxes ready, Event defs ready, Event comps ready, Event vers ready) bindings_idxes = [] # DT bindings documentation files +defs_idxes = {} # Idents definitions stored with (idx*1 000 000 + line) as the key. tags_done = False # True if all tags have been added to new_idxes @@ -240,6 +241,8 @@ def update_definitions(self, idxes): type = type.decode() line = int(line.decode()) + defs_idxes[idx*1000000+line] = ident + if db.defs.exists(ident): obj = db.defs.get(ident) elif lib.isIdent(ident): @@ -312,6 +315,8 @@ def update_references(self, idxes): tok = prefix + tok if (db.defs.exists(tok) and + not ( (idx*1000000+line_num) in defs_idxes and + defs_idxes[idx*1000000+line_num] == tok ) and (family != 'M' or tok.startswith(b'CONFIG_'))): # We only index CONFIG_??? in makefiles if tok in idents: From 10f08e33ead6b52dc2385693a8e45b9bdb58f6c3 Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Mon, 7 Dec 2020 12:20:59 +0100 Subject: [PATCH 233/529] test: Remove defs from refs results Signed-off-by: Maxime Chretien (MixLeNain) --- t/100-basic.t | 2 +- t/api_test.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/t/100-basic.t b/t/100-basic.t index d566c59a..89582b06 100644 --- a/t/100-basic.t +++ b/t/100-basic.t @@ -88,7 +88,7 @@ run_produces_ok('ident query (existent)', [$query_py, qw(v5.4 ident i2c_acpi_notify C)], [qr{^Symbol Definitions:}, qr{^Symbol References:}, { def => qr{drivers/i2c/i2c-core-acpi\.c.+\b402\b.+\bfunction\b} }, - { ref => qr{drivers/i2c/i2c-core-acpi\.c.+\b402,439} }, + { ref => qr{drivers/i2c/i2c-core-acpi\.c.+\b439} }, ], MUST_SUCCEED); diff --git a/t/api_test.py b/t/api_test.py index 5b052681..ad61d413 100644 --- a/t/api_test.py +++ b/t/api_test.py @@ -63,8 +63,7 @@ def test_existing_identifier(self): ], 'references': [ - {'path': 'drivers/i2c/i2c-core-of.c', 'line': '22,62,73', 'type': None}, - {'path': 'include/linux/i2c.h', 'line': '941,968', 'type': None} + {'path': 'drivers/i2c/i2c-core-of.c', 'line': '62,73', 'type': None} ], 'documentations': [] } From 763e4fd1aae6cbcb56b3df0c8e3df9e88a3819dd Mon Sep 17 00:00:00 2001 From: "Maxime Chretien (MixLeNain)" Date: Mon, 7 Dec 2020 14:19:44 +0100 Subject: [PATCH 234/529] update.py: use a constant for the key modifier of defs_idxes As Chris White said it, it's better to have only one line to change if we have to update that value. Signed-off-by: Maxime Chretien (MixLeNain) --- update.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/update.py b/update.py index 31b724b1..61c9a351 100755 --- a/update.py +++ b/update.py @@ -54,7 +54,8 @@ new_idxes = [] # (new idxes, Event idxes ready, Event defs ready, Event comps ready, Event vers ready) bindings_idxes = [] # DT bindings documentation files -defs_idxes = {} # Idents definitions stored with (idx*1 000 000 + line) as the key. +idx_key_mod = 1000000 +defs_idxes = {} # Idents definitions stored with (idx*idx_key_mod + line) as the key. tags_done = False # True if all tags have been added to new_idxes @@ -241,7 +242,7 @@ def update_definitions(self, idxes): type = type.decode() line = int(line.decode()) - defs_idxes[idx*1000000+line] = ident + defs_idxes[idx*idx_key_mod + line] = ident if db.defs.exists(ident): obj = db.defs.get(ident) @@ -315,8 +316,8 @@ def update_references(self, idxes): tok = prefix + tok if (db.defs.exists(tok) and - not ( (idx*1000000+line_num) in defs_idxes and - defs_idxes[idx*1000000+line_num] == tok ) and + not ( (idx*idx_key_mod + line_num) in defs_idxes and + defs_idxes[idx*idx_key_mod + line_num] == tok ) and (family != 'M' or tok.startswith(b'CONFIG_'))): # We only index CONFIG_??? in makefiles if tok in idents: From 0a466866430c460221f10f5464cfec72c1e110ee Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 8 Jan 2021 15:02:44 +0100 Subject: [PATCH 235/529] Remove 'page' identifier from the Linux blacklist - Don't want to skip this important structure in the Linux kernel - This fixes https://github.com/bootlin/elixir/issues/207 Signed-off-by: Michael Opdenacker --- lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib.py b/lib.py index 6c8f7f95..43ff0828 100755 --- a/lib.py +++ b/lib.py @@ -130,7 +130,6 @@ def decode(byte_object): b'ops', b'out', b'p', - b'page', b'pdev', b'port', b'priv', From f0842f8688fcc7ed4376aac6a2b8390e66029d4a Mon Sep 17 00:00:00 2001 From: Soenke Huster Date: Sat, 16 Oct 2021 22:51:14 +0200 Subject: [PATCH 236/529] Add BlueZ --- projects/bluez.sh | 19 +++++++++++++++++++ utils/index-all-repositories | 1 + utils/update-elixir-data | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 projects/bluez.sh diff --git a/projects/bluez.sh b/projects/bluez.sh new file mode 100644 index 00000000..0349235d --- /dev/null +++ b/projects/bluez.sh @@ -0,0 +1,19 @@ +# Elixir definitions for BlueZ +list_tags() +{ + echo "$tags" | + grep '^[0-9]' | + sed -e 's/^/v/' +} + +list_tags_h() +{ + echo "$tags" | + grep '^[0-9]' | + sed -r 's/^([0-9]*)\.([0-9]*)$/v\1 v\1.\2 \1.\2/' +} + +get_latest() +{ + git tag | grep '^[0-9]\.' | sort -V | tail -n 1 +} diff --git a/utils/index-all-repositories b/utils/index-all-repositories index ccf1885a..7deca4ec 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -96,3 +96,4 @@ index uclibc-ng git://uclibc-ng.org/git/uclibc-ng & index zephyr https://github.com/zephyrproject-rtos/zephyr & index toybox https://github.com/landley/toybox.git & index grub https://git.savannah.gnu.org/git/grub.git & +index bluez https://git.kernel.org/pub/scm/bluetooth/bluez.git & diff --git a/utils/update-elixir-data b/utils/update-elixir-data index 92b222e9..d57d63a9 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -30,7 +30,7 @@ if [ -z "$ELIXIR_INSTALL" ]; then exit 1 fi -for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono op-tee dpdk toybox grub; do +for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono op-tee dpdk toybox grub bluez; do echo "Processing project $root/$p ..." export LXR_DATA_DIR=$root/$p/data export LXR_REPO_DIR=$root/$p/repo From 8b0e6722b7935a225c30ce5cb6526249b2891944 Mon Sep 17 00:00:00 2001 From: Soenke Huster Date: Sun, 17 Oct 2021 13:05:11 +0200 Subject: [PATCH 237/529] Fix BlueZ tags --- projects/bluez.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/bluez.sh b/projects/bluez.sh index 0349235d..6ae7858f 100644 --- a/projects/bluez.sh +++ b/projects/bluez.sh @@ -2,8 +2,7 @@ list_tags() { echo "$tags" | - grep '^[0-9]' | - sed -e 's/^/v/' + grep '^[0-9]' } list_tags_h() From f77b14860e3945a3cae22a5947fd3aad320daab5 Mon Sep 17 00:00:00 2001 From: Soenke Huster Date: Sun, 17 Oct 2021 13:46:16 +0200 Subject: [PATCH 238/529] Sort BlueZ tags --- projects/bluez.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/bluez.sh b/projects/bluez.sh index 6ae7858f..dffa7db9 100644 --- a/projects/bluez.sh +++ b/projects/bluez.sh @@ -9,6 +9,7 @@ list_tags_h() { echo "$tags" | grep '^[0-9]' | + sort -rV | sed -r 's/^([0-9]*)\.([0-9]*)$/v\1 v\1.\2 \1.\2/' } From 2899eafe385f2946a4aa15e8ede4e3a1f9871aa4 Mon Sep 17 00:00:00 2001 From: Soenke Huster Date: Mon, 18 Oct 2021 11:28:00 +0200 Subject: [PATCH 239/529] BlueZ: Use sed -E for portability --- projects/bluez.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/bluez.sh b/projects/bluez.sh index dffa7db9..a7f5049a 100644 --- a/projects/bluez.sh +++ b/projects/bluez.sh @@ -10,7 +10,7 @@ list_tags_h() echo "$tags" | grep '^[0-9]' | sort -rV | - sed -r 's/^([0-9]*)\.([0-9]*)$/v\1 v\1.\2 \1.\2/' + sed -E 's/^([0-9]*)\.([0-9]*)$/v\1 v\1.\2 \1.\2/' } get_latest() From 514fa38ed8eced5bea900ef3748cd82d2306043b Mon Sep 17 00:00:00 2001 From: anatasluo Date: Wed, 18 May 2022 19:01:53 +0800 Subject: [PATCH 240/529] README: add configure step for SELinux and systemd To fix issues like #213, configure for SELinux is necessary. Signed-off-by: anatasluo --- README.adoc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.adoc b/README.adoc index 06ab44b9..8b99e249 100644 --- a/README.adoc +++ b/README.adoc @@ -289,6 +289,37 @@ Finally, start the httpd server. systemctl start httpd ---- + +== Configure SELinux policy + +When running systemd with SELinux enabled, httpd server can only visit limited directories. +If your /path/elixir-data/ is not one of these allowed directories, you will be responded with 500 status code. + +To allow httpd server to visit /path/elixir-data/, run following codes: +---- +chcon -R -t httpd_sys_rw_content_t /path/elixir-data/ +---- + +To check if it takes effect, run the following codes: +---- +ls -Z /path/elixir-data/ +---- + +In case you want to check SELinux log releated with httpd, run the following codes: +---- +audit2why -a | grep httpd | less +---- + +== Configure systemd log directory + +By default, the error log of elixir will be put in /tmp/elixir-errors. +However, systemd enables PrivateTmp by default. +And, the final error directory will be like /tmp/systemd-private-xxxxx-httpd.service-xxxx/tmp/elixir-errors. +If you want to disable it, configure httpd.service with the following attribute: +---- +PrivateTmp=false +---- + == Configure lighthttpd Here's a sample configuration for lighthttpd: From 14dcd032c4b5cb904038c7749f51fa5c96accdf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Mon, 4 Jul 2022 18:15:49 +0200 Subject: [PATCH 241/529] Front-end: make the whole width of all entries clickable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Théo Lebrun --- http/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/http/style.css b/http/style.css index c405b045..116900af 100644 --- a/http/style.css +++ b/http/style.css @@ -676,6 +676,7 @@ h2 { } .size { opacity: 0.6; + min-height: 1.5em; /* line_height + 2 * padding = 1.2em + 2 * 0.15em */ } From f5c6d1cd0a6e95b54b8add611b98f770d66bc664 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 21 Jul 2022 10:59:56 +0200 Subject: [PATCH 242/529] Fix tags for llvm Signed-off-by: Michael Opdenacker --- projects/llvm.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/projects/llvm.sh b/projects/llvm.sh index 0c279e49..1fcaaef6 100644 --- a/projects/llvm.sh +++ b/projects/llvm.sh @@ -1,8 +1,22 @@ # Elixir definitions for LLVM +list_tags() +{ + echo "$tags" | + tac | + grep ^llvmorg-[0-9]*[\.][0-9]* +} + list_tags_h() { echo "$tags" | + grep ^llvmorg | + grep -v init | tac | sed -r 's/^llvmorg-([0-9]*)\.([0-9]*)(.*)$/v\1 v\1.\2 llvmorg-\1.\2\3/' } + +get_latest() +{ + git tag | grep 'llvmorg' | grep -v init | sort -V | tail -n 1 +} From 00f52df9dccf82db63af92b6b071c5fafe049c03 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 29 Jul 2022 12:47:42 +0200 Subject: [PATCH 243/529] dpdk: also index the "stable" release repository Signed-off-by: Michael Opdenacker --- utils/index-all-repositories | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 7deca4ec..a11f7b22 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -82,7 +82,7 @@ index arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware index barebox https://git.pengutronix.de/git/barebox & index busybox git://git.busybox.net/busybox & index coreboot https://review.coreboot.org/coreboot.git & -index dpdk git://dpdk.org/dpdk & +index dpdk git://dpdk.org/dpdk git://dpdk.org/dpdk-stable & index glibc git://sourceware.org/git/glibc.git & index linux git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git https://github.com/bootlin/linux-history.git & index llvm https://github.com/llvm/llvm-project.git & From cb5fc9fbdc62d44b9cdcb090e05ceb57f410224d Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 23 Nov 2022 14:56:39 +0100 Subject: [PATCH 244/529] Update default HTML banner Signed-off-by: Michael Opdenacker --- templates/header.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/header.html b/templates/header.html index 50305c88..67908c91 100644 --- a/templates/header.html +++ b/templates/header.html @@ -1,15 +1,15 @@
    -

    Training sessions

    +

    Training courses

    Kernel and Embedded Linux

    - +
    -

    Next training sessions

    -
    Linux Kernel: March 16-20
    -
    Embedded Linux: May 11-15
    -
    and on-site sessions
    - +

    Bootlin training courses

    +
    Embedded Linux, kernel,
    +
    Yocto Project, Buildroot, real-time,
    +
    graphics, boot time, debugging...
    +

    From 76197b5973aec4f87e044caf30463333342bc389 Mon Sep 17 00:00:00 2001 From: Tamir Carmeli Date: Wed, 23 Nov 2022 16:20:27 +0200 Subject: [PATCH 245/529] BUGFIX: Don't double quote DT compatible strings (#229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both query.py and api.py call parse.quote(ident) which perform url encoding twice on the identifier. For instance, when ident="linux,kvm" the first encoding done in api.py changes the ident to GNU nano 6.2 /home/tamir/elixir/.git/COMMIT_EDITMSG * │ BUGFIX: Don't double quote DT compatible strings Both query.py and api.py call parse.quote(ident) which perform url encoding twice on the identifier. For instance, when ident="linux,kvm" the first encoding done in api.py changes the ident to 'wow%2Cey' but the second encoding done in query.py changes it to 'wow%252Cey' which is no longer valid. Remove the call to parse.quote on api.py as only one quoting is necessary, the one in query.py --- api/api.py | 3 --- query.py | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/api/api.py b/api/api.py index 19ab7321..7ba6b6ab 100755 --- a/api/api.py +++ b/api/api.py @@ -56,9 +56,6 @@ def on_get(self, req, resp, project, ident): else: family = 'C' - if family == 'B': #DT compatible strings are quoted in the database - ident = parse.quote(ident) - symbol_definitions, symbol_references, symbol_doccomments = query('ident', version, ident, family) resp.body = json.dumps( { diff --git a/query.py b/query.py index ae250253..7dd39f57 100755 --- a/query.py +++ b/query.py @@ -244,6 +244,7 @@ def get_idents_comps(version, ident): symbol_dts = [] symbol_docs = [] + # DT compatible strings are quoted in the database ident = parse.quote(ident) if not dts_comp_support or not db.comps.exists(ident): From 5d4a8d4e5d3d398742873f835b8e092e63bf8b11 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Wed, 23 Nov 2022 16:39:33 +0100 Subject: [PATCH 246/529] README.adoc: now use upstream version of Pygments No more need for a custom version, as our changes are now upstream. Signed-off-by: Michael Opdenacker --- README.adoc | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/README.adoc b/README.adoc index 8b99e249..951d0cd1 100644 --- a/README.adoc +++ b/README.adoc @@ -26,7 +26,7 @@ toc::[] * Python >= 3.6 * Git >= 1.9 -* The Jinja2 and Pygments (>= 2.2) Python libraries +* The Jinja2 and Pygments (>= 2.7) Python libraries * Berkeley DB (and its Python binding) * Universal Ctags * Perl (for non-greedy regexes and automated testing) @@ -68,11 +68,11 @@ A detailed diagram of the databases will be provided. Until then, just use the S == Install Dependences ____ -For RedHat/CentOS +For RedHat/Fedora/AlmaLinux ____ ---- -yum install python36-pip python36-pytest python36-jinja2 python36-bsddb3 python36-falcon git httpd perl perl-autodie jansson libyaml rh-python36-mod_wsgi +sudo dnf install python36-pip python36-pytest python36-jinja2 python36-bsddb3 python36-falcon python3-pygments git httpd perl perl-autodie jansson libyaml rh-python36-mod_wsgi ---- ____ @@ -80,7 +80,7 @@ For Debian ____ ---- -sudo apt install python3 python3-jinja2 python3-bsddb3 python3-falcon python3-pytest universal-ctags perl git apache2 libapache2-mod-wsgi-py3 libjansson4 +sudo apt install python3-jinja2 python3-bsddb3 python3-falcon python3-pytest python3-pygments universal-ctags perl git apache2 libapache2-mod-wsgi-py3 libjansson4 ---- To enable the REST API, follow the installation instructions on https://github.com/GrahamDumpleton/mod_wsgi[`mod_wsgi`] @@ -91,22 +91,6 @@ to know what packages Elixir needs in your favorite distribution. == Special dependencies -=== Device Tree and Kconfig Source syntax highlighting - -The service on https://elixir.bootlin.com relies on a modified version of https://pygments.org/[Pygments], -to enable syntax highlighting on Device Tree Source (DTS) files and on all Kconfig files. - -The changes have been sent upstream and should appear in future distributions. - -In the meantime, you can either rebuild this package from its source on -https://github.com/MaximeChretien/pygments/tree/dev[GitHub], or download this -https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl[binary module] -and install it as follows: - ----- -sudo pip3 install Pygments-2.6.1.elixir-py3-none-any.whl ----- - === Kconfig identifiers support The service on https://elixir.bootlin.com relies on a modified version of https://ctags.io/[Universal-ctags], From 67562314e13ef6dbaf3be3c5002d980a46ea8203 Mon Sep 17 00:00:00 2001 From: Ziyue Pan Date: Wed, 15 Mar 2023 17:54:13 +0800 Subject: [PATCH 247/529] fix typo in README --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 951d0cd1..02cf9cff 100644 --- a/README.adoc +++ b/README.adoc @@ -386,7 +386,7 @@ where all repositories are found. Docker files are provided in the `docker/` directory. To generate your own Docker image for indexing the sources of a project (for example for the Musl -project which is much faster to index that Linux), download the `Dockerfile` +project which is much faster to index than Linux), download the `Dockerfile` file for your target distribution and run: $ docker build -t elixir --build-arg GIT_REPO_URL=git://git.musl-libc.org/musl --build-arg PROJECT=musl . From 7c4886cf3c92b569765e0c991fb68fd2f34dafc4 Mon Sep 17 00:00:00 2001 From: Ziyue Pan Date: Thu, 23 Mar 2023 14:24:56 +0800 Subject: [PATCH 248/529] Style: fix redundant parentheses and typos (cherry picked from commit cd89684ce633c4997b328395cc882de3ab655475) --- README.adoc | 4 ++-- lib.py | 4 ++-- query.py | 6 +++--- update.py | 42 +++++++++++++++++++++--------------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.adoc b/README.adoc index 02cf9cff..7253a8fb 100644 --- a/README.adoc +++ b/README.adoc @@ -65,7 +65,7 @@ A detailed diagram of the databases will be provided. Until then, just use the S = Manual Installation -== Install Dependences +== Install Dependencies ____ For RedHat/Fedora/AlmaLinux @@ -289,7 +289,7 @@ To check if it takes effect, run the following codes: ls -Z /path/elixir-data/ ---- -In case you want to check SELinux log releated with httpd, run the following codes: +In case you want to check SELinux log related with httpd, run the following codes: ---- audit2why -a | grep httpd | less ---- diff --git a/lib.py b/lib.py index 43ff0828..bea8a168 100755 --- a/lib.py +++ b/lib.py @@ -228,13 +228,13 @@ def getFileFamily(filename): # Check if families are compatible # First argument can be a list of different families -# Second argument is the key for chossing the right array in the compatibility list +# Second argument is the key for choosing the right array in the compatibility list def compatibleFamily(file_family, requested_family): return any(item in file_family for item in compatibility_list[requested_family]) # Check if a macro is compatible with the requested family # First argument can be a list of different families -# Second argument is the key for chossing the right array in the compatibility list +# Second argument is the key for choosing the right array in the compatibility list def compatibleMacro(macro_family, requested_family): result = False for item in macro_family: diff --git a/query.py b/query.py index 7dd39f57..2584f857 100755 --- a/query.py +++ b/query.py @@ -60,11 +60,11 @@ def query(cmd, *args): num = len(taginfo) topmenu, submenu = 'FIXME', 'FIXME' - if (num == 1): + if num == 1: tag, = taginfo - elif (num == 2): + elif num == 2: submenu,tag = taginfo - elif (num ==3): + elif num ==3: topmenu,submenu,tag = taginfo if db.vers.exists(tag): diff --git a/update.py b/update.py index 61c9a351..ae662f93 100755 --- a/update.py +++ b/update.py @@ -142,7 +142,7 @@ def run(self): index = 0 while index < len(self.tag_buf): - if(index >= len(new_idxes)): + if index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() @@ -193,13 +193,13 @@ class UpdateDefs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateDefsElixir") self.index = start - self.inc = inc # Equivalient to the number of defs threads + self.inc = inc # Equivalent to the number of defs threads def run(self): global new_idxes, tags_done, tag_ready, tags_defs, tags_defs_lock - while(not (tags_done and self.index >= len(new_idxes))): - if(self.index >= len(new_idxes)): + while not (tags_done and self.index >= len(new_idxes)): + if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() @@ -225,7 +225,7 @@ def update_definitions(self, idxes): global hash_file_lock, defs_lock, tags_defs for idx in idxes: - if (idx % 1000 == 0): progress('defs: ' + str(idx), tags_defs[0]) + if idx % 1000 == 0: progress('defs: ' + str(idx), tags_defs[0]) with hash_file_lock: hash = db.hash.get(idx) @@ -261,13 +261,13 @@ class UpdateRefs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateRefsElixir") self.index = start - self.inc = inc # Equivalient to the number of refs threads + self.inc = inc # Equivalent to the number of refs threads def run(self): global new_idxes, tags_done, tags_refs, tags_refs_lock - while(not (tags_done and self.index >= len(new_idxes))): - if(self.index >= len(new_idxes)): + while not (tags_done and self.index >= len(new_idxes)): + if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() @@ -291,7 +291,7 @@ def update_references(self, idxes): global hash_file_lock, defs_lock, refs_lock, tags_refs for idx in idxes: - if (idx % 1000 == 0): progress('refs: ' + str(idx), tags_refs[0]) + if idx % 1000 == 0: progress('refs: ' + str(idx), tags_refs[0]) with hash_file_lock: hash = db.hash.get(idx) @@ -345,13 +345,13 @@ class UpdateDocs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateDocsElixir") self.index = start - self.inc = inc # Equivalient to the number of docs threads + self.inc = inc # Equivalent to the number of docs threads def run(self): global new_idxes, tags_done, tags_docs, tags_docs_lock - while(not (tags_done and self.index >= len(new_idxes))): - if(self.index >= len(new_idxes)): + while not (tags_done and self.index >= len(new_idxes)): + if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() @@ -374,7 +374,7 @@ def update_doc_comments(self, idxes): global hash_file_lock, docs_lock, tags_docs for idx in idxes: - if (idx % 1000 == 0): progress('docs: ' + str(idx), tags_docs[0]) + if idx % 1000 == 0: progress('docs: ' + str(idx), tags_docs[0]) with hash_file_lock: hash = db.hash.get(idx) @@ -404,13 +404,13 @@ class UpdateComps(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateCompsElixir") self.index = start - self.inc = inc # Equivalient to the number of comps threads + self.inc = inc # Equivalent to the number of comps threads def run(self): global new_idxes, tags_done, tags_comps, tags_comps_lock - while(not (tags_done and self.index >= len(new_idxes))): - if(self.index >= len(new_idxes)): + while not (tags_done and self.index >= len(new_idxes)): + if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() @@ -435,7 +435,7 @@ def update_compatibles(self, idxes): global hash_file_lock, comps_lock, tags_comps for idx in idxes: - if (idx % 1000 == 0): progress('comps: ' + str(idx), tags_comps[0]) + if idx % 1000 == 0: progress('comps: ' + str(idx), tags_comps[0]) with hash_file_lock: hash = db.hash.get(idx) @@ -471,13 +471,13 @@ class UpdateCompsDocs(Thread): def __init__(self, start, inc): Thread.__init__(self, name="UpdateCompsDocsElixir") self.index = start - self.inc = inc # Equivalient to the number of comps_docs threads + self.inc = inc # Equivalent to the number of comps_docs threads def run(self): global new_idxes, tags_done, tags_comps_docs, tags_comps_docs_lock - while(not (tags_done and self.index >= len(new_idxes))): - if(self.index >= len(new_idxes)): + while not (tags_done and self.index >= len(new_idxes)): + if self.index >= len(new_idxes): # Wait for new tags with tag_ready: tag_ready.wait() @@ -502,7 +502,7 @@ def update_compatibles_bindings(self, idxes): global hash_file_lock, comps_lock, comps_docs_lock, tags_comps_docs, bindings_idxes for idx in idxes: - if (idx % 1000 == 0): progress('comps_docs: ' + str(idx), tags_comps_docs[0]) + if idx % 1000 == 0: progress('comps_docs: ' + str(idx), tags_comps_docs[0]) if not idx in bindings_idxes: # Parse only bindings doc files continue From a627571baff358352cd0660185504f6e74e0e16d Mon Sep 17 00:00:00 2001 From: Ziyue Pan Date: Thu, 23 Mar 2023 14:45:34 +0800 Subject: [PATCH 249/529] Style: fix redundant parentheses and typos --- data.py | 12 ++++++------ http/filters/dtscomp.py | 2 +- http/web.py | 8 ++++---- query.py | 4 ++-- utils/speedtest.py | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/data.py b/data.py index ef68d69c..32e75642 100755 --- a/data.py +++ b/data.py @@ -68,9 +68,9 @@ def iter(self, dummy=False): type = defTypeR [type.decode()] line = int(line) family = family.decode() - yield(id, type, line, family) + yield id, type, line, family if dummy: - yield(maxId, None, None, None) + yield maxId, None, None, None def append(self, id, type, line, family): if type not in defTypeD: @@ -108,9 +108,9 @@ def iter(self, dummy=False): id, path = p.split(b' ',maxsplit=1) id = int(id) path = path.decode() - yield(id, path) + yield id, path if dummy: - yield(maxId, None) + yield maxId, None def append(self, id, path): p = str(id).encode() + b' ' + path + b'\n' @@ -133,9 +133,9 @@ def iter(self, dummy=False): b = int(b.decode()) c = c.decode() d = d.decode() - yield(b, c, d) + yield b, c, d if dummy: - yield(maxId, None, None) + yield maxId, None, None def append(self, id, lines, family): p = str(id) + ':' + lines + ':' + family + '\n' diff --git a/http/filters/dtscomp.py b/http/filters/dtscomp.py index 8d54276e..4030b024 100644 --- a/http/filters/dtscomp.py +++ b/http/filters/dtscomp.py @@ -1,6 +1,6 @@ # Elixir Python definitions for DT compatible strings support -if(dts_comp_support): +if dts_comp_support: exec(open('dtscompC.py').read()) exec(open('dtscompD.py').read()) exec(open('dtscompB.py').read()) diff --git a/http/web.py b/http/web.py index bd175e7b..6531123a 100755 --- a/http/web.py +++ b/http/web.py @@ -339,16 +339,16 @@ def print(arg, end='\n'): previous_type = '' types_count = {} - # Count occurences of each type before printing + # Count occurrences of each type before printing for symbol_definition in symbol_definitions: - if (symbol_definition.type in types_count): + if symbol_definition.type in types_count: types_count[symbol_definition.type] += 1 else: types_count[symbol_definition.type] = 1 for symbol_definition in symbol_definitions: - if (symbol_definition.type != previous_type) : - if (previous_type != ''): + if symbol_definition.type != previous_type: + if previous_type != '': print('') print('

    Defined in '+str(types_count[symbol_definition.type])+' files as a '+symbol_definition.type+':

    ') print('
      ') diff --git a/query.py b/query.py index 2584f857..9f5cc8b0 100755 --- a/query.py +++ b/query.py @@ -106,7 +106,7 @@ def query(cmd, *args): elif cmd == 'exist': - # Returns True if the requested file exists, overwise returns False + # Returns True if the requested file exists, otherwise returns False version = args[0] path = args[1] @@ -232,7 +232,7 @@ def query(cmd, *args): return get_idents_defs(version, ident, family) else: - return('Unknown subcommand: ' + cmd + '\n') + return 'Unknown subcommand: ' + cmd + '\n' def get_idents_comps(version, ident): diff --git a/utils/speedtest.py b/utils/speedtest.py index 91f7d18e..129e808a 100755 --- a/utils/speedtest.py +++ b/utils/speedtest.py @@ -96,10 +96,10 @@ def get_file(path, version): #Read arguments -if(len(sys.argv) > 1): - if(sys.argv[1] == '-v'): +if len(sys.argv) > 1: + if sys.argv[1] == '-v': verbose = True - elif (sys.argv[1] == '-h'): + elif sys.argv[1] == '-h': print("LXR_PROJ_DIR needs to be set before launching this script\n" + "Options :\n" + "-v Verbose mode (Show requests details)") @@ -107,7 +107,7 @@ def get_file(path, version): #Query init query = init_query(project) -if(query == None): +if query == None: exit() #Database test From 65e496b950e51fea50ec85ac7970fb613fac29c4 Mon Sep 17 00:00:00 2001 From: Andrew Soutar Date: Sun, 10 Oct 2021 20:06:46 -0400 Subject: [PATCH 250/529] vertical-align:top to fix line-number offset on some platforms --- http/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/http/style.css b/http/style.css index 116900af..b7813ca4 100644 --- a/http/style.css +++ b/http/style.css @@ -692,6 +692,10 @@ h2 { min-height: 100%; } +.lxrcode pre * { + vertical-align: top; +} + .lxrcode, .highlighttable, .linenodiv, From 8ffb3d9599c8ea036d0dac1d6a02bbc4fd66701d Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 23 Mar 2023 11:15:54 +0100 Subject: [PATCH 251/529] README.adoc: special version of "universal-ctags" no longer required Because required changes are now included in the official packages (at least in Ubuntu 22.04) Signed-off-by: Michael Opdenacker --- README.adoc | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/README.adoc b/README.adoc index 7253a8fb..866e1f46 100644 --- a/README.adoc +++ b/README.adoc @@ -89,33 +89,6 @@ and connect it to the apache installation as detailed in https://github.com/Grah To know which packages to install, you can also read the Docker files in the `docker/` directory to know what packages Elixir needs in your favorite distribution. -== Special dependencies - -=== Kconfig identifiers support - -The service on https://elixir.bootlin.com relies on a modified version of https://ctags.io/[Universal-ctags], -to enable indexing of Kconfig identifiers. - -The changes have been sent upstream and should appear in future distributions. - -In the meantime, you can either rebuild this package from its source on - https://github.com/universal-ctags/ctags[GitHub], or download this -https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb[deb binary package] -or https://bootlin.com/pub/elixir/universal-ctags-0+git~20e934e3-1.6.x86_64.rpm[rpm binary package] -and install it as follows: - ----- -sudo dpkg -i universal-ctags_0+git20200526-0ubuntu1_amd64.deb -or -sudo rpm -iv universal-ctags-0+git~20e934e3-1.6.x86_64.rpm ----- - -Then tell `apt` to hold this package and block future updates of the normal package: - ----- -sudo apt-mark hold universal-ctags ----- - == Download Elixir Project ---- From 8ac019c5470c2857641386d70e87d26955b8f23e Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 23 Mar 2023 11:22:18 +0100 Subject: [PATCH 252/529] .travis.yml: update for Ubuntu 22.04 - Now based on Python 3.10 - Now uses a standard "universal-ctags" package Signed-off-by: Michael Opdenacker --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d786b7c..814db782 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,16 +5,14 @@ os: - linux dist: - - bionic + - jammy python: - - "3.6" + - "3.10" before_install: - - sudo apt-get -y install libdb-dev python3-pytest libjansson4 + - sudo apt-get -y install libdb-dev python3-pytest libjansson4 universal-ctags - pip install jinja2 pygments bsddb3 falcon - - wget https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb - - sudo dpkg -i universal-ctags_0+git20200526-0ubuntu1_amd64.deb script: - prove -v From 2fca2acfffa531fda3565eea9437a358a6bff1f9 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 23 Mar 2023 12:01:16 +0100 Subject: [PATCH 253/529] utils/index-all-repositories: remove call to "filename" No longer available on Ubuntu 22.04, and not necessary anyway. Signed-off-by: Michael Opdenacker --- utils/index-all-repositories | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/index-all-repositories b/utils/index-all-repositories index a11f7b22..057ef3f9 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -32,8 +32,7 @@ index() { export LXR_REPO_DIR=$ELIXIR_ROOT/$project/repo mkdir -p $LXR_DATA_DIR - cd `dirname $LXR_REPO_DIR` - git clone --bare $master `filename $LXR_REPO_DIR` + git clone --bare $master $LXR_REPO_DIR if [ "$remote" != "" ] then From b5b0d720fb80f3207e804846162d807541969eba Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 23 Mar 2023 15:01:21 +0100 Subject: [PATCH 254/529] utils/index-all-repositories: update project repositories - Replace git:// by https://, more secure, more common - Update URLs when necessary Signed-off-by: Michael Opdenacker --- utils/index-all-repositories | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 057ef3f9..74aaa802 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -79,19 +79,19 @@ fi index amazon-freertos https://github.com/aws/amazon-freertos.git & index arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware & index barebox https://git.pengutronix.de/git/barebox & -index busybox git://git.busybox.net/busybox & +index busybox https://git.busybox.net/busybox & index coreboot https://review.coreboot.org/coreboot.git & -index dpdk git://dpdk.org/dpdk git://dpdk.org/dpdk-stable & -index glibc git://sourceware.org/git/glibc.git & -index linux git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git https://github.com/bootlin/linux-history.git & +index dpdk https://dpdk.org/git/dpdk https://dpdk.org/git/dpdk-stable & +index glibc https://sourceware.org/git/glibc.git & +index linux https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git https://github.com/bootlin/linux-history.git & index llvm https://github.com/llvm/llvm-project.git & index mesa https://gitlab.freedesktop.org/mesa/mesa.git & -index musl git://git.musl-libc.org/musl & +index musl https://git.musl-libc.org/git/musl & index ofono https://git.kernel.org/pub/scm/network/ofono/ofono.git & index op-tee https://github.com/OP-TEE/optee_os.git & -index qemu https://git.qemu.org/git/qemu.git & -index u-boot https://gitlab.denx.de/u-boot/u-boot.git & -index uclibc-ng git://uclibc-ng.org/git/uclibc-ng & +index qemu https://gitlab.com/qemu-project/qemu.git & +index u-boot https://source.denx.de/u-boot/u-boot.git & +index uclibc-ng https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git & index zephyr https://github.com/zephyrproject-rtos/zephyr & index toybox https://github.com/landley/toybox.git & index grub https://git.savannah.gnu.org/git/grub.git & From c3b8106f85cb00f21b617a25092c1dc6f31683c0 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Thu, 23 Mar 2023 15:14:41 +0100 Subject: [PATCH 255/529] utils/index-all-repositories: index projects sequentially Instead of all projects in parallel. Each indexing task runs parallel jobs anyway. This makes errors easier to spot and the whole process easier to stop. Signed-off-by: Michael Opdenacker --- utils/index-all-repositories | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 74aaa802..44388fdb 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -76,23 +76,23 @@ then exit 1 fi -index amazon-freertos https://github.com/aws/amazon-freertos.git & -index arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware & -index barebox https://git.pengutronix.de/git/barebox & -index busybox https://git.busybox.net/busybox & -index coreboot https://review.coreboot.org/coreboot.git & -index dpdk https://dpdk.org/git/dpdk https://dpdk.org/git/dpdk-stable & -index glibc https://sourceware.org/git/glibc.git & -index linux https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git https://github.com/bootlin/linux-history.git & -index llvm https://github.com/llvm/llvm-project.git & -index mesa https://gitlab.freedesktop.org/mesa/mesa.git & -index musl https://git.musl-libc.org/git/musl & -index ofono https://git.kernel.org/pub/scm/network/ofono/ofono.git & -index op-tee https://github.com/OP-TEE/optee_os.git & -index qemu https://gitlab.com/qemu-project/qemu.git & -index u-boot https://source.denx.de/u-boot/u-boot.git & -index uclibc-ng https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git & -index zephyr https://github.com/zephyrproject-rtos/zephyr & -index toybox https://github.com/landley/toybox.git & -index grub https://git.savannah.gnu.org/git/grub.git & -index bluez https://git.kernel.org/pub/scm/bluetooth/bluez.git & +index amazon-freertos https://github.com/aws/amazon-freertos.git +index arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware +index barebox https://git.pengutronix.de/git/barebox +index busybox https://git.busybox.net/busybox +index coreboot https://review.coreboot.org/coreboot.git +index dpdk https://dpdk.org/git/dpdk https://dpdk.org/git/dpdk-stable +index glibc https://sourceware.org/git/glibc.git +index llvm https://github.com/llvm/llvm-project.git +index mesa https://gitlab.freedesktop.org/mesa/mesa.git +index musl https://git.musl-libc.org/git/musl +index ofono https://git.kernel.org/pub/scm/network/ofono/ofono.git +index op-tee https://github.com/OP-TEE/optee_os.git +index qemu https://gitlab.com/qemu-project/qemu.git +index u-boot https://source.denx.de/u-boot/u-boot.git +index uclibc-ng https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git +index zephyr https://github.com/zephyrproject-rtos/zephyr +index toybox https://github.com/landley/toybox.git +index grub https://git.savannah.gnu.org/git/grub.git +index bluez https://git.kernel.org/pub/scm/bluetooth/bluez.git +index linux https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git https://github.com/bootlin/linux-history.git From 462b8741c51615dd16cacd714c94edfd49f277cc Mon Sep 17 00:00:00 2001 From: Artem Chernyshev Date: Thu, 6 Apr 2023 14:35:48 +0300 Subject: [PATCH 256/529] ignoring env, venv and idea --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 5cc790bf..7960b501 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ __pycache__ tags /.cache/ +.idea/ +env/ +venv/ # Web-specific http/images From ddfc4e8d2384a91130d6479bbb490e578f6575ab Mon Sep 17 00:00:00 2001 From: Artem Chernyshev Date: Fri, 7 Apr 2023 12:37:46 +0300 Subject: [PATCH 257/529] replace iterating over hardcoded project names Self explanatory. Replacing it with env variable --- utils/update-elixir-data | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/utils/update-elixir-data b/utils/update-elixir-data index d57d63a9..a8ee37d0 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -17,11 +17,8 @@ # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . -root=$1 - -if [ ! -e "$root" ] -then - echo "ERROR: directory not found: $root" +if [ -z "$LXR_PROJ_DIR" ]; then + echo "ERROR: LXR_PROJ_DIR environment variable not set" exit 1 fi @@ -30,10 +27,10 @@ if [ -z "$ELIXIR_INSTALL" ]; then exit 1 fi -for p in linux u-boot busybox zephyr musl barebox uclibc-ng arm-trusted-firmware amazon-freertos qemu glibc coreboot llvm mesa ofono op-tee dpdk toybox grub bluez; do - echo "Processing project $root/$p ..." - export LXR_DATA_DIR=$root/$p/data - export LXR_REPO_DIR=$root/$p/repo +for dir_name in $LXR_PROJ_DIR/*; do + echo "Processing project $dir_name ..." + export LXR_DATA_DIR=$dir_name/data + export LXR_REPO_DIR=$dir_name/repo cd $LXR_REPO_DIR git fetch --all --tags From 26a47b6111355f505dd549e552afd00e10edec20 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 5 May 2023 10:26:08 +0200 Subject: [PATCH 258/529] Use git "bare" cloning in README.adoc and Dockerfile(s) This saves storage space Signed-off-by: Michael Opdenacker --- README.adoc | 2 +- docker/centos/Dockerfile | 2 +- docker/debian/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index 866e1f46..c0333b3a 100644 --- a/README.adoc +++ b/README.adoc @@ -125,7 +125,7 @@ First clone the master tree released by Linus Torvalds: ---- cd /path/elixir-data/linux -git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git repo +git clone --bare https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git repo ---- Then, you should also declare a `stable` remote branch corresponding to the `stable` tree, to get all release updates: diff --git a/docker/centos/Dockerfile b/docker/centos/Dockerfile index 92842168..f71deb20 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos/Dockerfile @@ -70,7 +70,7 @@ RUN \ RUN \ mkdir -p /srv/elixir-data/$PROJECT/repo && \ mkdir -p /srv/elixir-data/$PROJECT/data && \ - git clone "${GIT_REPO_URL}" /srv/elixir-data/$PROJECT/repo/ + git clone --bare "${GIT_REPO_URL}" /srv/elixir-data/$PROJECT/repo/ ENV LXR_REPO_DIR /srv/elixir-data/$PROJECT/repo ENV LXR_DATA_DIR /srv/elixir-data/$PROJECT/data diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 96756265..d8cc7c1b 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -58,7 +58,7 @@ RUN \ RUN \ mkdir -p /srv/elixir-data/$PROJECT/repo && \ mkdir -p /srv/elixir-data/$PROJECT/data && \ - git clone "${GIT_REPO_URL}" /srv/elixir-data/$PROJECT/repo/ + git clone --bare "${GIT_REPO_URL}" /srv/elixir-data/$PROJECT/repo/ ENV LXR_REPO_DIR /srv/elixir-data/$PROJECT/repo ENV LXR_DATA_DIR /srv/elixir-data/$PROJECT/data From 02ae89c917c6194743b72db1bba2fb5ab0aa8fb2 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 5 May 2023 15:11:51 +0200 Subject: [PATCH 259/529] utils: figure out Elixir install directory No more need for ELIXIR_INSTALL variable Signed-off-by: Michael Opdenacker --- utils/index-all-repositories | 9 ++------- utils/update-elixir-data | 7 ++----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 44388fdb..186ae7b0 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -62,6 +62,8 @@ index() { ./update.py $ELIXIR_THREADS } +export ELIXIR_INSTALL=$(dirname $(dirname $(readlink -f "$0"))) + if [ "$ELIXIR_ROOT" = "" ] then echo "Error: ELIXIR_ROOT environment variable not set" @@ -69,13 +71,6 @@ then exit 1 fi -if [ "$ELIXIR_INSTALL" = "" ] -then - echo "Error: ELIXIR_INSTALL environment variable not set" - echo "It's where Elixir code is stored" - exit 1 -fi - index amazon-freertos https://github.com/aws/amazon-freertos.git index arm-trusted-firmware https://github.com/ARM-software/arm-trusted-firmware index barebox https://git.pengutronix.de/git/barebox diff --git a/utils/update-elixir-data b/utils/update-elixir-data index a8ee37d0..a1018d31 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -17,16 +17,13 @@ # You should have received a copy of the GNU Affero General Public License # along with Elixir. If not, see . +export ELIXIR_INSTALL=$(dirname $(dirname $(readlink -f "$0"))) + if [ -z "$LXR_PROJ_DIR" ]; then echo "ERROR: LXR_PROJ_DIR environment variable not set" exit 1 fi -if [ -z "$ELIXIR_INSTALL" ]; then - echo "ERROR: ELIXIR_INSTALL environment variable not set" - exit 1 -fi - for dir_name in $LXR_PROJ_DIR/*; do echo "Processing project $dir_name ..." export LXR_DATA_DIR=$dir_name/data From c156433520e07fbf3c8340f4b931b66bd99751a4 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 5 May 2023 15:17:27 +0200 Subject: [PATCH 260/529] utils: set ELIXIR_THREADS to number of CPUs This way, you have a correct behaviour even without setting ELIXIR_THREADS Signed-off-by: Michael Opdenacker --- utils/common.sh | 24 ++++++++++++++++++++++++ utils/index-all-repositories | 1 + utils/update-elixir-data | 1 + 3 files changed, 26 insertions(+) create mode 100755 utils/common.sh diff --git a/utils/common.sh b/utils/common.sh new file mode 100755 index 00000000..3bc403c0 --- /dev/null +++ b/utils/common.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# This file is part of Elixir, a source code cross-referencer. +# +# Copyright (C) 2019--2023 Michael Opdenacker and contributors +# +# Elixir is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Elixir is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Elixir. If not, see . + +if [ "$ELIXIR_THREADS" = "" ] +then + # Set number of threads to the number of CPU cores + # available to the current process + export ELIXIR_THREADS=`nproc` +fi diff --git a/utils/index-all-repositories b/utils/index-all-repositories index 186ae7b0..dff042fe 100755 --- a/utils/index-all-repositories +++ b/utils/index-all-repositories @@ -63,6 +63,7 @@ index() { } export ELIXIR_INSTALL=$(dirname $(dirname $(readlink -f "$0"))) +. $ELIXIR_INSTALL/utils/common.sh if [ "$ELIXIR_ROOT" = "" ] then diff --git a/utils/update-elixir-data b/utils/update-elixir-data index a1018d31..de948ec4 100755 --- a/utils/update-elixir-data +++ b/utils/update-elixir-data @@ -18,6 +18,7 @@ # along with Elixir. If not, see . export ELIXIR_INSTALL=$(dirname $(dirname $(readlink -f "$0"))) +. $ELIXIR_INSTALL/utils/common.sh if [ -z "$LXR_PROJ_DIR" ]; then echo "ERROR: LXR_PROJ_DIR environment variable not set" From e1dd2b4881aba5479d240c9073199cdd7eaa031e Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 5 May 2023 15:50:43 +0200 Subject: [PATCH 261/529] update.py: simplify and optimize thread management Instead of having a complicated thread count scheme and ending up wasting time waiting for the docs task to finish with a too low thread count at the end, let's run all tasks with same number of threads, which by default is the number of CPUs in the system. This way, the indexing work always uses as many CPUs as possible, especially when same tasks are finished before the others. The OS shouldn't bother if we try to run more threads than the actual number of CPUs. Set the ELIXIR_THREADS environment variable if you want to use a lower number of threads, typically on a desktop machine on which you want some responsiveness. Signed-off-by: Michael Opdenacker --- update.py | 58 ++++++++++--------------------------------------------- 1 file changed, 10 insertions(+), 48 deletions(-) diff --git a/update.py b/update.py index ae662f93..59f74c12 100755 --- a/update.py +++ b/update.py @@ -39,8 +39,6 @@ db = data.DB(lib.getDataDir(), readonly=False, dtscomp=dts_comp_support) -# Number of cpu threads (+2 for version indexing) -cpu = 10 threads_list = [] hash_file_lock = Lock() # Lock for db.hash and db.file @@ -544,36 +542,9 @@ def progress(msg, current): # Check number of threads arg if len(argv) >= 2 and argv[1].isdigit() : - cpu = int(argv[1]) - - if cpu < 5 : - cpu = 5 - -# Distribute threads among functions using the following rules : -# There are more (or equal) refs threads than others -# There are more (or equal) defs threads than docs or comps threads -# Example : if cpu=6 : defs=1, refs=2, docs=1, comps=1, comps_docs=1 -# if cpu=7 : defs=2, refs=2, docs=1, comps=1, comps_docs=1 -# if cpu=8 : defs=2, refs=3, docs=1, comps=1, comps_docs=1 -# if cpu=11: defs=2, refs=3, docs=2, comps=2, comps_docs=2 -quo, rem = divmod(cpu, 5) -num_th_refs = quo -num_th_defs = quo -num_th_docs = quo - -# If DT bindings support is enabled, use $quo threads for each of the 2 threads -# Otherwise add them to the remaining threads -if dts_comp_support: - num_th_comps = quo - num_th_comps_docs = quo -else : - num_th_comps = 0 - num_th_comps_docs = 0 - rem += 2*quo - -quo, rem = divmod(rem, 2) -num_th_defs += quo -num_th_refs += quo + rem + cpus = int(argv[1]) +else: + cpus = os.cpu_count() tag_buf = [] for tag in scriptLines('list-tags'): @@ -591,22 +562,13 @@ def progress(msg, current): threads_list.append(UpdateIds(tag_buf)) threads_list.append(UpdateVersions(tag_buf)) -# Define defs threads -for i in range(num_th_defs): - threads_list.append(UpdateDefs(i, num_th_defs)) -# Define refs threads -for i in range(num_th_refs): - threads_list.append(UpdateRefs(i, num_th_refs)) -# Define docs threads -for i in range(num_th_docs): - threads_list.append(UpdateDocs(i, num_th_docs)) -# Define comps threads -for i in range(num_th_comps): - threads_list.append(UpdateComps(i, num_th_comps)) -# Define comps_docs threads -for i in range(num_th_comps_docs): - threads_list.append(UpdateCompsDocs(i, num_th_comps_docs)) - +# Define threads +for i in range(cpus): + threads_list.append(UpdateDefs(i, cpus)) + threads_list.append(UpdateRefs(i, cpus)) + threads_list.append(UpdateDocs(i, cpus)) + threads_list.append(UpdateComps(i, cpus)) + threads_list.append(UpdateCompsDocs(i, cpus)) # Start to process tags threads_list[0].start() From 1e3517f2615f17f894a01b27f3c702a719ec8104 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Fri, 5 May 2023 16:21:30 +0200 Subject: [PATCH 262/529] README.adoc: update infos about threads Signed-off-by: Michael Opdenacker --- README.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index c0333b3a..0cae9184 100644 --- a/README.adoc +++ b/README.adoc @@ -156,7 +156,7 @@ cd /usr/local/elixir/ == Create Database ---- -./update.py +./update.py ---- ____ @@ -330,7 +330,7 @@ we're proposing to use a script like `utils/update-elixir-data` which is called through a daily cron job. You can set `$ELIXIR_THREADS` if you want to change the number of threads used by -update.py for indexing (default is 10 and minimum is 5). +update.py for indexing (by default the number of CPUs on your system). == Keeping git repository disk usage under control @@ -481,7 +481,7 @@ add `dts_comp_support=1` at the beginning of `projects/.sh`. You are now ready to generate Elixir's database for your new project: - ./update.py + ./update.py You can then check that Elixir works through your http server. From a8581e5fd4ace40f07f4e26098ed3efdeb7b876c Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 8 May 2023 07:18:31 +0200 Subject: [PATCH 263/529] Revert "update.py: simplify and optimize thread management" This gave suboptimal results in terms of indexing time, plus a stuck job. Should be investigated further before changing this. This reverts commit e1dd2b4881aba5479d240c9073199cdd7eaa031e. --- update.py | 58 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/update.py b/update.py index 59f74c12..ae662f93 100755 --- a/update.py +++ b/update.py @@ -39,6 +39,8 @@ db = data.DB(lib.getDataDir(), readonly=False, dtscomp=dts_comp_support) +# Number of cpu threads (+2 for version indexing) +cpu = 10 threads_list = [] hash_file_lock = Lock() # Lock for db.hash and db.file @@ -542,9 +544,36 @@ def progress(msg, current): # Check number of threads arg if len(argv) >= 2 and argv[1].isdigit() : - cpus = int(argv[1]) -else: - cpus = os.cpu_count() + cpu = int(argv[1]) + + if cpu < 5 : + cpu = 5 + +# Distribute threads among functions using the following rules : +# There are more (or equal) refs threads than others +# There are more (or equal) defs threads than docs or comps threads +# Example : if cpu=6 : defs=1, refs=2, docs=1, comps=1, comps_docs=1 +# if cpu=7 : defs=2, refs=2, docs=1, comps=1, comps_docs=1 +# if cpu=8 : defs=2, refs=3, docs=1, comps=1, comps_docs=1 +# if cpu=11: defs=2, refs=3, docs=2, comps=2, comps_docs=2 +quo, rem = divmod(cpu, 5) +num_th_refs = quo +num_th_defs = quo +num_th_docs = quo + +# If DT bindings support is enabled, use $quo threads for each of the 2 threads +# Otherwise add them to the remaining threads +if dts_comp_support: + num_th_comps = quo + num_th_comps_docs = quo +else : + num_th_comps = 0 + num_th_comps_docs = 0 + rem += 2*quo + +quo, rem = divmod(rem, 2) +num_th_defs += quo +num_th_refs += quo + rem tag_buf = [] for tag in scriptLines('list-tags'): @@ -562,13 +591,22 @@ def progress(msg, current): threads_list.append(UpdateIds(tag_buf)) threads_list.append(UpdateVersions(tag_buf)) -# Define threads -for i in range(cpus): - threads_list.append(UpdateDefs(i, cpus)) - threads_list.append(UpdateRefs(i, cpus)) - threads_list.append(UpdateDocs(i, cpus)) - threads_list.append(UpdateComps(i, cpus)) - threads_list.append(UpdateCompsDocs(i, cpus)) +# Define defs threads +for i in range(num_th_defs): + threads_list.append(UpdateDefs(i, num_th_defs)) +# Define refs threads +for i in range(num_th_refs): + threads_list.append(UpdateRefs(i, num_th_refs)) +# Define docs threads +for i in range(num_th_docs): + threads_list.append(UpdateDocs(i, num_th_docs)) +# Define comps threads +for i in range(num_th_comps): + threads_list.append(UpdateComps(i, num_th_comps)) +# Define comps_docs threads +for i in range(num_th_comps_docs): + threads_list.append(UpdateCompsDocs(i, num_th_comps_docs)) + # Start to process tags threads_list[0].start() From 47a6001385077ee1fd562b1be58bb0d3d3fa7e81 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Tue, 15 Nov 2022 10:24:34 +0200 Subject: [PATCH 264/529] parse linux system calls definitions fixes #222 --- script.sh | 1 + t/100-basic.t | 7 +++++++ t/tree/syscall_define.c | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 t/tree/syscall_define.c diff --git a/script.sh b/script.sh index e915aa5a..011e0956 100755 --- a/script.sh +++ b/script.sh @@ -171,6 +171,7 @@ parse_defs_C() # Parse function macros, e.g., in .S files perl -ne '/^\s*ENTRY\((\w+)\)/ and print "$1 function $.\n"' "$full_path" + perl -ne '/^SYSCALL_DEFINE[0-9]\(\s*(\w+)\W/ and print "sys_$1 function $.\n"' "$full_path" rm "$full_path" rmdir $tmp diff --git a/t/100-basic.t b/t/100-basic.t index 89582b06..39a22ffa 100644 --- a/t/100-basic.t +++ b/t/100-basic.t @@ -122,6 +122,13 @@ run_produces_ok('ident query (ENTRY that should not be detected, #150)', ], MUST_SUCCEED); +run_produces_ok('ident query (existent, #228)', + [$query_py, qw(v5.4 ident sys_init_module C)], + [qr{^Symbol Definitions:}, qr{^Symbol References:}, + { def => qr{syscall_define\.c.+\b1\b.+\bfunction\b} } + ], + MUST_SUCCEED); + # Spot-check some files run_produces_ok('file query (nonexistent)', diff --git a/t/tree/syscall_define.c b/t/tree/syscall_define.c new file mode 100644 index 00000000..fab93883 --- /dev/null +++ b/t/tree/syscall_define.c @@ -0,0 +1,3 @@ +SYSCALL_DEFINE3(init_module) +{ +} From 5d39bb2ffaa84087c1251d9878afb0faa80873a8 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Mon, 15 May 2023 15:40:49 +0200 Subject: [PATCH 265/529] Bump version to 2.2 Signed-off-by: Michael Opdenacker --- templates/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/layout.html b/templates/layout.html index 86788b1f..29c86cb5 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -65,7 +65,7 @@

      Versions

      From 4bea126f6f1ca647a4c32033d4c17d6c94b8d829 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Thu, 7 Dec 2023 12:43:10 +0100 Subject: [PATCH 266/529] Fix and refactor Debian Dockerfile * Pin base version to debian:bookworm * Install falcon with apt * Use pipx to install Pygments, since global package installation with pip is not supported anymore * Add elixir-data to git safe directories so that git ran from script.sh stops complaining * Update repo permissions so that git ran from the cgi script stops complaining * Move apache configuration to an external file for better readability --- docker/debian/000-default.conf | 24 ++++++++++++++++++++++++ docker/debian/Dockerfile | 26 ++++++++++++-------------- 2 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 docker/debian/000-default.conf diff --git a/docker/debian/000-default.conf b/docker/debian/000-default.conf new file mode 100644 index 00000000..3d5e2499 --- /dev/null +++ b/docker/debian/000-default.conf @@ -0,0 +1,24 @@ + + Options +ExecCGI + AllowOverride None + Require all granted + SetEnv PYTHONIOENCODING utf-8 + SetEnv LXR_PROJ_DIR /srv/elixir-data + + + SetHandler wsgi-script + Require all granted + SetEnv PYTHONIOENCODING utf-8 + SetEnv LXR_PROJ_DIR /srv/elixir-data + +AddHandler cgi-script .py + + ServerName MY_LOCAL_IP + DocumentRoot /usr/local/elixir/http + WSGIScriptAlias /api /usr/local/elixir/api/api.py + AllowEncodedSlashes On + RewriteEngine on + RewriteRule "^/$" "/linux/latest/source" [R] + RewriteRule "^/(?!api|acp).*/(source|ident|search)" "/web.py" [PT] + RewriteRule "^/acp" "/autocomplete.py" [PT] + diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index d8cc7c1b..77de212f 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:latest +FROM debian:bookworm ARG GIT_REPO_URL ARG PROJECT @@ -11,14 +11,17 @@ RUN \ RUN \ echo "repo url to index: ${GIT_REPO_URL}" + RUN \ apt-get update && \ apt-get -y install \ python3 \ python3-pip \ + python3-falcon \ python3-jinja2 \ python3-bsddb3 \ python3-pytest \ + pipx \ perl \ git \ apache2 \ @@ -27,12 +30,6 @@ RUN \ libyaml-0-2 \ wget -RUN \ - pip3 install falcon - -RUN \ - ln -s /usr/bin/pytest-3 /usr/bin/pytest - RUN \ wget https://bootlin.com/pub/elixir/universal-ctags_0+git20200526-0ubuntu1_amd64.deb @@ -43,7 +40,7 @@ RUN \ wget https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl RUN \ - pip3 install Pygments-2.6.1.elixir-py3-none-any.whl + pipx install ./Pygments-2.6.1.elixir-py3-none-any.whl RUN \ git config --global user.email 'elixir@dummy.com' && \ @@ -53,12 +50,11 @@ RUN \ git clone https://github.com/bootlin/elixir.git /usr/local/elixir/ RUN \ - mkdir -p /srv/elixir-data/ - -RUN \ + mkdir -p /srv/elixir-data/ && \ mkdir -p /srv/elixir-data/$PROJECT/repo && \ mkdir -p /srv/elixir-data/$PROJECT/data && \ - git clone --bare "${GIT_REPO_URL}" /srv/elixir-data/$PROJECT/repo/ + git clone --bare "${GIT_REPO_URL}" /srv/elixir-data/$PROJECT/repo/ && \ + git config --global --add safe.directory /srv/elixir-data/$PROJECT/repo ENV LXR_REPO_DIR /srv/elixir-data/$PROJECT/repo ENV LXR_DATA_DIR /srv/elixir-data/$PROJECT/data @@ -66,12 +62,14 @@ ENV LXR_DATA_DIR /srv/elixir-data/$PROJECT/data RUN \ cd /usr/local/elixir/ && \ ./script.sh list-tags && \ - python3 -u ./update.py + python3 -u ./update.py && \ + chown -R www-data:www-data /srv/elixir-data/$PROJECT/repo # apache elixir config, see elixir README # make apache less stricter about cgitb spam headers +COPY ./docker/debian/000-default.conf /etc/apache2/sites-available/000-default.conf + RUN \ - echo PERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9odHRwLz4KICAgIE9wdGlvbnMgK0V4ZWNDR0kKICAgIEFsbG93T3ZlcnJpZGUgTm9uZQogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KPERpcmVjdG9yeSAvdXNyL2xvY2FsL2VsaXhpci9hcGkvPgogICAgU2V0SGFuZGxlciB3c2dpLXNjcmlwdAogICAgUmVxdWlyZSBhbGwgZ3JhbnRlZAogICAgU2V0RW52IFBZVEhPTklPRU5DT0RJTkcgdXRmLTgKICAgIFNldEVudiBMWFJfUFJPSl9ESVIgL3Nydi9lbGl4aXItZGF0YQo8L0RpcmVjdG9yeT4KQWRkSGFuZGxlciBjZ2ktc2NyaXB0IC5weQo8VmlydHVhbEhvc3QgKjo4MD4KICAgIFNlcnZlck5hbWUgTVlfTE9DQUxfSVAKICAgIERvY3VtZW50Um9vdCAvdXNyL2xvY2FsL2VsaXhpci9odHRwCiAgICBXU0dJU2NyaXB0QWxpYXMgL2FwaSAvdXNyL2xvY2FsL2VsaXhpci9hcGkvYXBpLnB5CiAgICBBbGxvd0VuY29kZWRTbGFzaGVzIE9uCiAgICBSZXdyaXRlRW5naW5lIG9uCiAgICBSZXdyaXRlUnVsZSAiXi8kIiAiL2xpbnV4L2xhdGVzdC9zb3VyY2UiIFtSXQogICAgUmV3cml0ZVJ1bGUgIl4vKD8hYXBpfGFjcCkuKi8oc291cmNlfGlkZW50fHNlYXJjaCkiICIvd2ViLnB5IiBbUFRdCiAgICBSZXdyaXRlUnVsZSAiXi9hY3AiICIvYXV0b2NvbXBsZXRlLnB5IiBbUFRdCjwvVmlydHVhbEhvc3Q+Cg== | base64 -d > /etc/apache2/sites-available/000-default.conf && \ echo -e "\nHttpProtocolOptions Unsafe" >> /etc/apache2/apache.conf && \ a2enmod cgi rewrite From 051218622e69d47c5a9efd129da7ab355178b3b1 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Thu, 14 Dec 2023 01:37:58 +0100 Subject: [PATCH 267/529] Fix pygments installation --- docker/debian/Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 77de212f..0a851f84 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -14,13 +14,14 @@ RUN \ RUN \ apt-get update && \ - apt-get -y install \ + apt-get --no-install-recommends -y install \ python3 \ python3-pip \ python3-falcon \ python3-jinja2 \ python3-bsddb3 \ python3-pytest \ + python3-pygments- \ pipx \ perl \ git \ @@ -40,7 +41,7 @@ RUN \ wget https://bootlin.com/pub/elixir/Pygments-2.6.1.elixir-py3-none-any.whl RUN \ - pipx install ./Pygments-2.6.1.elixir-py3-none-any.whl + pip3 install ./Pygments-2.6.1.elixir-py3-none-any.whl --break-system-packages RUN \ git config --global user.email 'elixir@dummy.com' && \ From 5871fa6dca91fd4c06c9f0da311d154bee1a5a11 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Thu, 7 Dec 2023 16:29:16 +0100 Subject: [PATCH 268/529] Add dynamic references Initial implementation of a new mode in which clicking on an identifier creates a HTML popup instead of redirecting to a new page. Skip popup if ctrl/shift/meta is held. Fix generateDocComments Add ident css class to more ident links (docs, dtb, config...) Make popup positioning more predicatble * Attempt to move the popup if a part of it is rendered outside of the viewport * Make popup full-width on smartphone/small tablet devices Hide popup on escape click Add "use strict" and fix undeclared variable errors --- http/dynamic-references.js | 211 ++++++++++++++++++++++++++++++++++ http/filters/defconfig.py | 2 +- http/filters/dtscompB.py | 2 +- http/filters/dtscompC.py | 2 +- http/filters/dtscompD.py | 2 +- http/filters/ident.py | 2 +- http/filters/kconfigidents.py | 2 +- http/style.css | 37 ++++++ templates/layout.html | 4 + 9 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 http/dynamic-references.js diff --git a/http/dynamic-references.js b/http/dynamic-references.js new file mode 100644 index 00000000..08591882 --- /dev/null +++ b/http/dynamic-references.js @@ -0,0 +1,211 @@ +"use strict"; + +async function fetchIdent(project, ident, version, family) { + return fetch(`/api/ident/${project}/${ident}?version=${version}&family=${family}`) + .then(r => r.json()); +} + +/* + "definitions": [ + { + "path": "arch/x86_64/kstat.h", + "line": 1, + "type": "struct" + } + ], + "references": [ + { + "path": "src/stat/fstatat.c", + "line": "78,142", + "type": null + } + ], + "documentations": [ + { + "path": "Documentation/devicetree/bindings/arm/arm,cci-400.yaml", + "line": "15,17,18", + "type": null + } + ] + */ + +function generateSymbolDefinitionsHTML(symbolDefinitions, version) { + let result = ""; + let typesCount = {}; + let previous_type = ""; + + if(symbolDefinitions.length == 0) { + return '

      No definitions found in the database

      '; + } + + for(let symbolDefinition of symbolDefinitions) { + if (symbolDefinition.type in typesCount) { + typesCount[symbolDefinition.type] += 1; + } else { + typesCount[symbolDefinition.type] = 1; + } + } + + for (let sd of symbolDefinitions) { + if (sd.type != previous_type) { + if (previous_type != '') { + result += '
    '; + } + result += '

    Defined in ' + typesCount[sd.type].toString() + ' files as a ' + sd.type + ':

    '; + result += ''; + + return result; +} + +function generateSymbolReferencesHTML(symbolReferences, version) { + let result = ""; + + if(symbolReferences.length == 0) { + return '

    No references found in the database

    '; + } + + result += '

    Referenced in ' + symbolReferences.length.toString() + ' files:

    '; + result += '
      '; + for (let sr of symbolReferences) { + let ln = sr.line.split(','); + if (ln.length == 1) { + let n = ln[0]; + result += `
    • ${sr.path}, line ${n}`; + } else { + if(symbolReferences.length > 100) { + let n = ln.length; + result += `
    • ${sr.path}, ${n} times`; + } else { + result += `
    • ${sr.path}`; + result += '
        ' + for(let n of ln) { + result += `
      • line ${n}` + } + result += '
      ' + } + } + } + result += '
    ' + return result; +} + +function generateDocCommentsHTML(symbolDocComments, version) { + let result = ""; + if (symbolDocComments.length == 0) { + return result; + } + result += '

    Documented in ' + symbolDocComments.length.toString() + ' files:

    '; + result += '
      '; + for(let sd of symbolDocComments) { + let ln = sd.line.split(','); + if(ln.length == 1) { + let n = ln[0]; + result += `
    • ${sd.path}, line ${n}`; + } else { + if(symbolDocComments.length > 100) { + let n = ln.length; + result += `
    • ${sd.path}, ${n} times`; + } else { + result += `
    • ${sd.path}`; + result += '
        '; + for(let n of ln) { + result += `
      • line ${n}`; + } + result += '
      '; + } + } + result += '
    '; + } + return result; +} + +function generateReferencesHTML(data, version) { + let symbolDefinitions = data["definitions"]; + let symbolReferences = data["references"]; + let symbolDocumentations = data["documentations"]; + return '
    ' + + generateSymbolDefinitionsHTML(symbolDefinitions, version) + + generateDocCommentsHTML(symbolDocumentations, version) + + generateSymbolReferencesHTML(symbolReferences, version) + + '
    '; +} + +function showPopup(referencePopup, target) { + let targetRect = target.getBoundingClientRect(); + let x = target.offsetLeft; + let y = target.offsetTop + targetRect.height; + + referencePopup.style.visibility = "visible"; + referencePopup.style.display = "block"; + referencePopup.style.left = `${x}px`; + referencePopup.style.top = `${y}px`; + referencePopup.scrollTop = 0; + referencePopup.scrollLeft = 0; + + let referenceRect = referencePopup.getBoundingClientRect(); + + if((referenceRect.top + referenceRect.height) > window.innerHeight) { + referencePopup.style.top = `${target.offsetTop - referenceRect.height}px`; + } + + if((referenceRect.left + referenceRect.width) > window.innerWidth) { + x -= ((referenceRect.left + referenceRect.width) - window.innerWidth); + referencePopup.style.left = `${x}px`; + } +} + +function hidePopup(referencePopup) { + referencePopup.style.visibility = "hidden"; + referencePopup.style.display = "none"; +} + +document.addEventListener("DOMContentLoaded", _ => { + let referencePopup = document.getElementById("reference-popup"); + + document.body.querySelectorAll(".ident").forEach(el => { + el.addEventListener("click", async ev => { + if (ev.ctrlKey || ev.metaKey || ev.shiftKey) { + return; + } + + ev.preventDefault(); + let splitPath = ev.target.pathname.split("/"); + let [_, project, version, family, _i, ident] = splitPath; + let result = await fetchIdent(project, ident, version, family); + + referencePopup.innerHTML = generateReferencesHTML(result, version); + showPopup(referencePopup, ev.target); + }); + }); + + referencePopup.addEventListener("click", ev => ev.stopPropagation()); + + document.body.addEventListener("click", _ => hidePopup(referencePopup)); + + document.body.addEventListener("keydown", ev => { + if (ev.key === "Escape") { + hidePopup(referencePopup); + } + }); +}); diff --git a/http/filters/defconfig.py b/http/filters/defconfig.py index 4a0fa38d..d49291a2 100644 --- a/http/filters/defconfig.py +++ b/http/filters/defconfig.py @@ -9,7 +9,7 @@ def keep_defconfigidents(m): def replace_defconfigidents(m): i = defconfigidents[decode_number(m.group(1)) - 1] - return ''+i+'' + return ''+i+'' defconfigident_filters = { 'case': 'filename_extension', diff --git a/http/filters/dtscompB.py b/http/filters/dtscompB.py index ac61c70e..7382063b 100644 --- a/http/filters/dtscompB.py +++ b/http/filters/dtscompB.py @@ -14,7 +14,7 @@ def keep_dtscompB(m): def replace_dtscompB(m): i = dtscompB[decode_number(m.group(1)) - 1] - return ''+i+'' + return ''+i+'' dtscompB_filters = { 'case': 'path', diff --git a/http/filters/dtscompC.py b/http/filters/dtscompC.py index e9c03dab..1fbf88c9 100644 --- a/http/filters/dtscompC.py +++ b/http/filters/dtscompC.py @@ -9,7 +9,7 @@ def keep_dtscompC(m): def replace_dtscompC(m): i = dtscompC[decode_number(m.group(1)) - 1] - return ''+i+'' + return ''+i+'' dtscompC_filters = { 'case': 'extension', diff --git a/http/filters/dtscompD.py b/http/filters/dtscompD.py index 9ec509e6..d4d72add 100644 --- a/http/filters/dtscompD.py +++ b/http/filters/dtscompD.py @@ -15,7 +15,7 @@ def keep_dtscompD(m): def replace_dtscompD(m): i = dtscompD[decode_number(m.group(1)) - 1] - return ''+i+'' + return ''+i+'' dtscompD_filters = { 'case': 'extension', diff --git a/http/filters/ident.py b/http/filters/ident.py index 26ba88b5..55a45527 100644 --- a/http/filters/ident.py +++ b/http/filters/ident.py @@ -8,7 +8,7 @@ def keep_idents(m): def replace_idents(m): i = idents[decode_number(m.group(2)) - 1] - return str(m.group(1) or '') + ''+i+'' + return str(m.group(1) or '') + ''+i+'' ident_filters = { 'case': 'any', diff --git a/http/filters/kconfigidents.py b/http/filters/kconfigidents.py index 3402865e..c451d96a 100644 --- a/http/filters/kconfigidents.py +++ b/http/filters/kconfigidents.py @@ -14,7 +14,7 @@ def replace_kconfigidents(m): if family == 'K': n = n[7:] - return str(m.group(1) or '') + ''+n+'' + return str(m.group(1) or '') + ''+n+'' kconfigident_filters = { 'case': 'any', diff --git a/http/style.css b/http/style.css index b7813ca4..a448b7f7 100644 --- a/http/style.css +++ b/http/style.css @@ -439,6 +439,43 @@ h2 { padding: 0; } +/* reference popup */ + +#reference-popup-wrapper { + position: absolute; + display: inline-block; +} + +#reference-popup { + position: absolute; + visibility: hidden; + display: none; + border: 2px solid black; + background: white; + padding: 5px; + z-index: 10; + overflow-y: scroll; + min-height: 25vh; + min-width: 40vw; + max-height: 50vh; + max-width: 75vw; + + @media only screen and (max-width: 769px) { + min-width: 100vw; + } +} + +#reference-popup::-webkit-scrollbar-corner { + background: #666; +} + +#reference-popup::-webkit-scrollbar-track { + background: #666; +} + +#reference-popup::-webkit-scrollbar-thumb { + background: #ccc; +} /* if javascript on/off */ diff --git a/templates/layout.html b/templates/layout.html index 29c86cb5..dc3a0b6d 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -40,6 +40,9 @@
    +
    +
    +
    {{main}}
    From 237b4bb549f57d49c2845293fdd05e40589b6252 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Tue, 16 Jul 2024 21:49:57 +0200 Subject: [PATCH 305/529] Move breadcrumb links generation to layout template --- http/web.py | 10 +++------- templates/layout.html | 6 +++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/http/web.py b/http/web.py index f488f248..166c5043 100755 --- a/http/web.py +++ b/http/web.py @@ -381,10 +381,10 @@ def generate_source_page(q, basedir, parsed_path): # Generate breadcrumbs path_split = path.split('/')[1:] path_temp = '' - links = [] + breadcrumb_links = [] for p in path_split: path_temp += '/'+p - links.append(''+p+'') + breadcrumb_links.append((p, version + '/source' + path_temp)) # Generate title title_suffix = project.capitalize()+' source code ('+tag+') - Bootlin' @@ -409,7 +409,7 @@ def generate_source_page(q, basedir, parsed_path): 'ident': '', 'family': 'A', - 'breadcrumb': '/', + 'breadcrumb_links': breadcrumb_links, 'title': title, 'versions': q.query('versions'), @@ -419,9 +419,6 @@ def generate_source_page(q, basedir, parsed_path): 'main': outputBuffer.getvalue(), } - if links: - data['breadcrumb'] += '/'.join(links) - template = environment.get_template('layout.html') return (status, template.render(data)) @@ -567,7 +564,6 @@ def generate_ident_page(q, basedir, parsed_path): 'ident': ident, 'family': family, - 'breadcrumb': '/', 'title': ident+' identifier - '+title_suffix, 'versions': q.query('versions'), diff --git a/templates/layout.html b/templates/layout.html index db421a61..e998a4c3 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -21,7 +21,11 @@
    - {{main}} + {% if symbol_sections is defined %} +
    + {% if symbol_sections|length != 0 %} + {% for section in symbol_sections %} + {% if 'symbols' in section %} + {% for type, symbols in section['symbols'].items() %} +

    {{ section['title'] }} in {{ symbols|length }} files + {{- ' as a '+type if type != '_unknown' else '' -}}:

    + + {% endfor %} + {% else %} + {{ section['message'] }} + {% endif %} + {% endfor %} + {% else %} +

    Identifier not used

    + {% endif %} +
    + {% else %} + {{ main }} + {% endif %}

\n') for l in lines: type, name, size, perm = l.split(' ') - if type == 'null': - continue - elif type == 'tree': - size = '' - path2 = path+'/'+name - name = name - elif type == 'blob': - size = size+' bytes' - path2 = path+'/'+name + if type == 'tree': + dir_entries.append(('tree', name, f"{path}/{name}", '')) + if type == 'blob': + file_path = f"{path}/{name}" + # 120000 permission means it's a symlink if perm == '120000': - # 120000 permission means it's a symlink - # So we need to handle that correctly dir_name = os.path.dirname(path) - rel_path = q.query('file', tag, path2) + rel_path = q.query('file', tag, file_path) if dir_name != '/': dir_name += '/' - path2 = os.path.abspath(dir_name + rel_path) - - name = name + ' -> ' + path2 - elif type == 'back': - size = '' - path2 = os.path.dirname(path[:-1]) - if path2 == '/': path2 = '' - name = 'Parent directory' + file_path = os.path.abspath(dir_name + rel_path) + name = name + ' -> ' + file_path - print(' \n') - print(' \n') - print(' \n') - print(' \n') + dir_entries.append(('blob', name, file_path, f"{size} bytes")) - print('
'+name+''+size+'
', end='') - print('
') + template_ctx = { + 'dir_entries': dir_entries, + } + template = environment.get_template('tree.html') elif type == 'blob': - - import pygments - import pygments.lexers - import pygments.formatters - - fdir, fname = os.path.split(path) - filename, extension = os.path.splitext(fname) - extension = extension[1:].lower() - family = q.query('family', fname) - - # globals required by filters - # this dict is also modified by filters - most introduce new, global variables - # that are later used by prefunc/postfunc - filter_ctx = { - **default_globals, - "os": os, - "parse": parse, - "re": re, - "dts_comp_support": q.query('dts-comp'), - - "version": version, - "family": family, - "path": path, - "tag": tag, - "q": q, + template_ctx = { + 'code': generate_source(q, code, path, version, tag, project), } - - # Source common filter definitions - os.chdir('filters') - exec(open("common.py").read(), filter_ctx) - - # Source project specific filters - f = project + '.py' - if os.path.isfile(f): - exec(open(f).read(), filter_ctx) - os.chdir('..') - - filters = filter_ctx["filters"] - - # Apply filters - for f in filters: - c = f['case'] - if (c == 'any' or - (c == 'filename' and filename in f['match']) or - (c == 'extension' and extension in f['match']) or - (c == 'path' and fdir.startswith(tuple(f['match']))) or - (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): - - apply_filter = True - - if 'path_exceptions' in f: - for p in f['path_exceptions']: - if re.match(p, path): - apply_filter = False - break - - if apply_filter: - code = sub(f ['prerex'], f ['prefunc'], code, flags=re.MULTILINE) - - - try: - lexer = pygments.lexers.guess_lexer_for_filename(path, code) - except: - lexer = pygments.lexers.get_lexer_by_name('text') - - lexer.stripnl = False - formatter = pygments.formatters.HtmlFormatter(linenos=True, anchorlinenos=True) - result = pygments.highlight(code, lexer, formatter) - - # Replace line numbers by links to the corresponding line in the current file - result = sub('href="#-(\d+)', 'name="L\\1" id="L\\1" href="'+version+'/source'+path+'#L\\1', result) - - for f in filters: - c = f['case'] - if (c == 'any' or - (c == 'filename' and filename in f['match']) or - (c == 'extension' and extension in f['match']) or - (c == 'path' and fdir.startswith(tuple(f['match']))) or - (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): - - result = sub(f ['postrex'], f ['postfunc'], result) - - print('
' + result + '
') - + template = environment.get_template('source.html') # Generate breadcrumbs path_split = path.split('/')[1:] @@ -400,6 +397,8 @@ def generate_source_page(q, basedir, parsed_path): # Create template context data = { + **template_ctx, + 'baseurl': '/' + project + '/', 'tag': tag, 'version': version, @@ -415,11 +414,8 @@ def generate_source_page(q, basedir, parsed_path): 'versions': q.query('versions'), 'url': url, 'current_tag': tag, - - 'content': outputBuffer.getvalue(), } - template = environment.get_template('source.html') return (status, template.render(data)) diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 00000000..46449f35 --- /dev/null +++ b/templates/error.html @@ -0,0 +1,10 @@ +{% extends "layout.html" %} + +{% block main %} + +
+

{{ error_title }}

+ {{ error_details }} +
+ +{% endblock %} diff --git a/templates/source.html b/templates/source.html index 1058d093..a4164a42 100644 --- a/templates/source.html +++ b/templates/source.html @@ -1,5 +1,7 @@ {% extends "layout.html" %} {% block main %} - {{ content }} +
+ {{ code }} +
{% endblock %} diff --git a/templates/tree.html b/templates/tree.html new file mode 100644 index 00000000..e68513cd --- /dev/null +++ b/templates/tree.html @@ -0,0 +1,18 @@ +{% extends "layout.html" %} + +{% block main %} + +
+ + + {% for type, name, path, size in dir_entries %} + + + + + {% endfor %} + +
{{ name }}{{ size }}
+
+ +{% endblock %} From 585d3e3243c748d473b7d88a58994bbe5b4e40bb Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Wed, 17 Jul 2024 14:40:03 +0200 Subject: [PATCH 311/529] Move directory entries generation to a new function * Remove redundant code parameter in generate_source --- http/web.py | 91 ++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/http/web.py b/http/web.py index 0b6ba31d..1eb1564e 100755 --- a/http/web.py +++ b/http/web.py @@ -222,11 +222,13 @@ def get_directories(basedir): directories.append(filename) return sorted(directories) -def generate_source(q, code, path, version, tag, project): +def generate_source(q, path, version, tag, project): import pygments import pygments.lexers import pygments.formatters + code = q.query('file', tag, path) + fdir, fname = os.path.split(path) filename, extension = os.path.splitext(fname) extension = extension[1:].lower() @@ -306,6 +308,39 @@ def generate_source(q, code, path, version, tag, project): return result +def get_directory_entries(q, tag, path): + dir_entries = [] + lines = q.query('dir', tag, path) + + if path != '': + back_path = os.path.dirname(path[:-1]) + if back_path == '/': + back_path = '' + dir_entries.append(('back', 'Parent directory', back_path, '')) + + for l in lines: + type, name, size, perm = l.split(' ') + + if type == 'tree': + dir_entries.append(('tree', name, f"{path}/{name}", '')) + if type == 'blob': + file_path = f"{path}/{name}" + + # 120000 permission means it's a symlink + if perm == '120000': + dir_name = os.path.dirname(path) + rel_path = q.query('file', tag, file_path) + + if dir_name != '/': + dir_name += '/' + + file_path = os.path.abspath(dir_name + rel_path) + name = name + ' -> ' + file_path + + dir_entries.append(('blob', name, file_path, f"{size} bytes")) + + return dir_entries + # Generates response (status code and optionally HTML) of the `source` route # q: Query object # basedir: path to data directory, ex: "/srv/elixir-data" @@ -319,61 +354,25 @@ def generate_source_page(q, basedir, parsed_path): path = parsed_path.path tag = parse.unquote(version) - lines = [] - type = q.query('type', tag, path) - if len(type) > 0: - if type == 'tree': - lines += q.query('dir', tag, path) - elif type == 'blob': - code = q.query('file', tag, path) - else: - template_ctx = { - 'error_title': 'This file does not exist.', - } - template = environment.get_template('error.html') - status = 404 if type == 'tree': - dir_entries = [] - - if path != '': - back_path = os.path.dirname(path[:-1]) - if back_path == '/': - back_path = '' - dir_entries.append(('back', 'Parent directory', back_path, '')) - - for l in lines: - type, name, size, perm = l.split(' ') - - if type == 'tree': - dir_entries.append(('tree', name, f"{path}/{name}", '')) - if type == 'blob': - file_path = f"{path}/{name}" - - # 120000 permission means it's a symlink - if perm == '120000': - dir_name = os.path.dirname(path) - rel_path = q.query('file', tag, file_path) - - if dir_name != '/': - dir_name += '/' - - file_path = os.path.abspath(dir_name + rel_path) - name = name + ' -> ' + file_path - - dir_entries.append(('blob', name, file_path, f"{size} bytes")) - template_ctx = { - 'dir_entries': dir_entries, + 'dir_entries': get_directory_entries(q, tag, path), } template = environment.get_template('tree.html') - elif type == 'blob': template_ctx = { - 'code': generate_source(q, code, path, version, tag, project), + 'code': generate_source(q, path, version, tag, project), } template = environment.get_template('source.html') + else: + status = 404 + template_ctx = { + 'error_title': 'This file does not exist.', + } + template = environment.get_template('error.html') + # Generate breadcrumbs path_split = path.split('/')[1:] From f08aeb7b76ff7eb2ac73e102bd522fad0e15382a Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Wed, 17 Jul 2024 14:56:10 +0200 Subject: [PATCH 312/529] Split layout into sidebar and topbar --- templates/layout.html | 81 ++++-------------------------------------- templates/sidebar.html | 47 ++++++++++++++++++++++++ templates/topbar.html | 24 +++++++++++++ 3 files changed, 78 insertions(+), 74 deletions(-) create mode 100644 templates/sidebar.html create mode 100644 templates/topbar.html diff --git a/templates/layout.html b/templates/layout.html index 02e788ae..da224242 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -3,13 +3,13 @@ - {{title}} + {{ title }} - + @@ -18,30 +18,7 @@
{% include 'header.html' %} -
- - -
+ {% include 'topbar.html' %}
@@ -51,60 +28,16 @@ {% block main %} {% endblock %}
- + {% include "sidebar.html" %}
- + diff --git a/templates/sidebar.html b/templates/sidebar.html new file mode 100644 index 00000000..3d69d6db --- /dev/null +++ b/templates/sidebar.html @@ -0,0 +1,47 @@ + diff --git a/templates/topbar.html b/templates/topbar.html new file mode 100644 index 00000000..c086b7c9 --- /dev/null +++ b/templates/topbar.html @@ -0,0 +1,24 @@ +
+ + +
From 554bb11381e53f86559c161790edc3d0aa5da740 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Wed, 17 Jul 2024 23:31:20 +0200 Subject: [PATCH 313/529] Refactor directory tree generation to use a namedtuple * Fix #302 - Symlinks redirect to invalid paths (but only the directory tree part) * Move more view-related logic to the template --- http/web.py | 40 ++++++++++++++++++++++++---------------- query.py | 3 +++ templates/tree.html | 26 ++++++++++++++++++++------ 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/http/web.py b/http/web.py index 1eb1564e..a0f79613 100755 --- a/http/web.py +++ b/http/web.py @@ -308,36 +308,38 @@ def generate_source(q, path, version, tag, project): return result +# Represents a file entry in git tree +# type: either tree (directory), blob (file) or symlink +# name: filename of the file +# path: path of the file, path to the target in case of symlinks +# size: int, file size in bytes, None for directories and symlinks +DirectoryEntry = namedtuple('DirectoryEntry', 'type, name, path, size') + +# Returns a list of DirectoryEntry objects with information about files in directory +# q - Query object +# tag - request repository tag +# path - path to the directory def get_directory_entries(q, tag, path): dir_entries = [] lines = q.query('dir', tag, path) - if path != '': - back_path = os.path.dirname(path[:-1]) - if back_path == '/': - back_path = '' - dir_entries.append(('back', 'Parent directory', back_path, '')) - for l in lines: type, name, size, perm = l.split(' ') if type == 'tree': dir_entries.append(('tree', name, f"{path}/{name}", '')) - if type == 'blob': + elif type == 'blob': file_path = f"{path}/{name}" # 120000 permission means it's a symlink if perm == '120000': - dir_name = os.path.dirname(path) - rel_path = q.query('file', tag, file_path) - - if dir_name != '/': - dir_name += '/' + dir_path = path if path.endswith('/') else path + '/' + link_contents = q.get_file_raw(tag, file_path) + link_target_path = os.path.abspath(dir_path + link_contents) - file_path = os.path.abspath(dir_name + rel_path) - name = name + ' -> ' + file_path - - dir_entries.append(('blob', name, file_path, f"{size} bytes")) + dir_entries.append(('symlink', name, link_target_path, size)) + else: + dir_entries.append(('blob', name, file_path, size)) return dir_entries @@ -357,8 +359,14 @@ def generate_source_page(q, basedir, parsed_path): type = q.query('type', tag, path) if type == 'tree': + back_path = os.path.dirname(path[:-1]) + if back_path == '/': + back_path = '' + template_ctx = { 'dir_entries': get_directory_entries(q, tag, path), + 'current_path': path, + 'back_path': back_path, } template = environment.get_template('tree.html') elif type == 'blob': diff --git a/query.py b/query.py index a9f60b6c..87f0ae94 100755 --- a/query.py +++ b/query.py @@ -253,6 +253,9 @@ def query(self, cmd, *args): else: return 'Unknown subcommand: ' + cmd + '\n' + def get_file_raw(self, version, path): + return decode(self.script('get-file', version, path)) + def get_idents_comps(self, version, ident): # DT bindings compatible strings are handled differently diff --git a/templates/tree.html b/templates/tree.html index e68513cd..c25eb098 100644 --- a/templates/tree.html +++ b/templates/tree.html @@ -5,12 +5,26 @@
- {% for type, name, path, size in dir_entries %} - - - - - {% endfor %} + {% if current_path != '' %} + + + + {% endif %} + {% for type, name, path, size in dir_entries %} + + + + + {% endfor %}
{{ name }}{{ size }}
Parent directory
+ + {% if type == 'symlink' %} + {{ name }} -> {{ path }} + {% else %} + {{ name }} + {% endif %} + + {{ size }} bytes
From d1f70679dff725bf55b38a407c9a8551dd1129d3 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Wed, 17 Jul 2024 23:36:53 +0200 Subject: [PATCH 314/529] Remove fake print from web.py Replace all instances of realprint with print --- http/web.py | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/http/web.py b/http/web.py index a0f79613..17541619 100755 --- a/http/web.py +++ b/http/web.py @@ -29,7 +29,6 @@ import re import sys from collections import OrderedDict, namedtuple -from io import StringIO from re import search, sub from urllib import parse import jinja2 @@ -38,13 +37,6 @@ from lib import validFamily from query import Query, SymbolInstance -realprint = print -outputBuffer = StringIO() - -def print(arg, end='\n'): - global outputBuffer - outputBuffer.write(arg + end) - script_dir = os.path.dirname(os.path.realpath(__file__)) templates_dir = os.path.join(script_dir, '../templates/') loader = jinja2.FileSystemLoader(templates_dir) @@ -105,7 +97,7 @@ def handle_source_url(path, _): parsed_path = parse_source_path(path) if parsed_path is None: - realprint("Error: failed to parse path in handle_source_url", path, file=sys.stderr) + print("Error: failed to parse path in handle_source_url", path, file=sys.stderr) return (404, "Failed to parse path") query = get_query(os.environ['LXR_PROJ_DIR'], parsed_path.project) @@ -179,7 +171,7 @@ def handle_ident_post_form(parsed_path, form): def handle_ident_url(path, params): parsed_path = parse_ident_path(path) if parsed_path is None: - realprint("Error: failed to parse path in handle_ident_url", path, file=sys.stderr) + print("Error: failed to parse path in handle_ident_url", path, file=sys.stderr) return (404, "Failed to parse path") status = handle_ident_post_form(parsed_path, params) @@ -527,31 +519,31 @@ def generate_ident_page(q, basedir, parsed_path): if result is not None: if result[0] == 200: - realprint('Content-Type: text/html;charset=utf-8\n') - realprint(result[1], end='') + print('Content-Type: text/html;charset=utf-8\n') + print(result[1], end='') elif result[0] == 301: - realprint('Status: 301 Moved Permanently') - realprint('Location: '+ result[1] +'\n') + print('Status: 301 Moved Permanently') + print('Location: '+ result[1] +'\n') exit() elif result[0] == 302: - realprint('Status: 302 Found') - realprint('Location: '+ result[1] +'\n') + print('Status: 302 Found') + print('Location: '+ result[1] +'\n') exit() elif result[0] == 400: - realprint('Status: 400 Bad Request\n') + print('Status: 400 Bad Request\n') exit() elif result[0] == 404: - realprint('Status: 404 Not Found') - realprint('Content-Type: text/html;charset=utf-8\n') - realprint(result[1], end='') + print('Status: 404 Not Found') + print('Content-Type: text/html;charset=utf-8\n') + print(result[1], end='') else: - realprint('Status: 500 Internal Server Error') - realprint('Content-Type: text/html;charset=utf-8\n') - realprint('Error - route returned an unknown status code', result, file=sys.stderr) - realprint('Unknown error - check error logs for details\n') + print('Status: 500 Internal Server Error') + print('Content-Type: text/html;charset=utf-8\n') + print('Error - route returned an unknown status code', result, file=sys.stderr) + print('Unknown error - check error logs for details\n') else: - realprint('Status: 500 Internal Server Error') - realprint('Content-Type: text/html;charset=utf-8\n') - realprint('Error - route returned None', file=sys.stderr) - realprint('Unknown error - check error logs for details\n') + print('Status: 500 Internal Server Error') + print('Content-Type: text/html;charset=utf-8\n') + print('Error - route returned None', file=sys.stderr) + print('Unknown error - check error logs for details\n') From ba5c4007a2dc92680c97b2527fe6cf340109b7cc Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Thu, 18 Jul 2024 00:16:40 +0200 Subject: [PATCH 315/529] Move code formatting to format_code --- http/web.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/http/web.py b/http/web.py index 17541619..9d9643e3 100755 --- a/http/web.py +++ b/http/web.py @@ -214,11 +214,22 @@ def get_directories(basedir): directories.append(filename) return sorted(directories) -def generate_source(q, path, version, tag, project): +# Guesses file format based on filename, returns code formatted as HTML +def format_code(filename, code): import pygments import pygments.lexers import pygments.formatters + try: + lexer = pygments.lexers.guess_lexer_for_filename(filename, code) + except pygments.util.ClassNotFound: + lexer = pygments.lexers.get_lexer_by_name('text') + + lexer.stripnl = False + formatter = pygments.formatters.HtmlFormatter(linenos=True, anchorlinenos=True) + return pygments.highlight(code, lexer, formatter) + +def generate_source(q, path, version, tag, project): code = q.query('file', tag, path) fdir, fname = os.path.split(path) @@ -276,17 +287,10 @@ def generate_source(q, path, version, tag, project): code = sub(f ['prerex'], f ['prefunc'], code, flags=re.MULTILINE) - try: - lexer = pygments.lexers.guess_lexer_for_filename(path, code) - except: - lexer = pygments.lexers.get_lexer_by_name('text') - - lexer.stripnl = False - formatter = pygments.formatters.HtmlFormatter(linenos=True, anchorlinenos=True) - result = pygments.highlight(code, lexer, formatter) + html_code_block = format_code(fname, code) # Replace line numbers by links to the corresponding line in the current file - result = sub('href="#-(\d+)', 'name="L\\1" id="L\\1" href="'+version+'/source'+path+'#L\\1', result) + html_code_block = sub('href="#-(\d+)', 'name="L\\1" id="L\\1" href="'+version+'/source'+path+'#L\\1', html_code_block) for f in filters: c = f['case'] @@ -296,9 +300,10 @@ def generate_source(q, path, version, tag, project): (c == 'path' and fdir.startswith(tuple(f['match']))) or (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): - result = sub(f ['postrex'], f ['postfunc'], result) + html_code_block = sub(f ['postrex'], f ['postfunc'], html_code_block) + + return html_code_block - return result # Represents a file entry in git tree # type: either tree (directory), blob (file) or symlink From fabbc52d36583430931a1e7b14627ffa8cf81550 Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Tue, 30 Jul 2024 13:55:21 +0200 Subject: [PATCH 316/529] Move filter path exceptions handling filter_applies Note that this also causes postfunc to not run on files where prefunc was not applied because filter_applies returned False. This could be a mistake, although it seems that the only filter that uses path exceptions (cppthinc filter modified from linux filter) should be compatible with this behavior. --- http/web.py | 58 +++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/http/web.py b/http/web.py index 9d9643e3..f8f9ee62 100755 --- a/http/web.py +++ b/http/web.py @@ -229,11 +229,35 @@ def format_code(filename, code): formatter = pygments.formatters.HtmlFormatter(linenos=True, anchorlinenos=True) return pygments.highlight(code, lexer, formatter) +# Return true if filter can be applied to file based on path of the file +def filter_applies(filter, path): + if 'path_exceptions' in filter: + for p in filter['path_exceptions']: + if re.match(p, path): + return False + + dir, filename = os.path.split(path) + filename, extension = os.path.splitext(filename) + + c = filter['case'] + if c == 'any': + return True + elif c == 'filename': + return filename in filter['match'] + elif c == 'extension': + return extension in filter['match'] + elif c == 'path': + return dir.startswith(tuple(filter['match'])) + elif c == 'filename_extension': + return filename.endswith(tuple(filter['match'])) + else: + raise ValueError('Invalid filter case', filter['case']) + def generate_source(q, path, version, tag, project): code = q.query('file', tag, path) - fdir, fname = os.path.split(path) - filename, extension = os.path.splitext(fname) + _, fname = os.path.split(path) + _, extension = os.path.splitext(fname) extension = extension[1:].lower() family = q.query('family', fname) @@ -268,24 +292,8 @@ def generate_source(q, path, version, tag, project): # Apply filters for f in filters: - c = f['case'] - if (c == 'any' or - (c == 'filename' and filename in f['match']) or - (c == 'extension' and extension in f['match']) or - (c == 'path' and fdir.startswith(tuple(f['match']))) or - (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): - - apply_filter = True - - if 'path_exceptions' in f: - for p in f['path_exceptions']: - if re.match(p, path): - apply_filter = False - break - - if apply_filter: - code = sub(f ['prerex'], f ['prefunc'], code, flags=re.MULTILINE) - + if filter_applies(f, path): + code = sub(f['prerex'], f['prefunc'], code, flags=re.MULTILINE) html_code_block = format_code(fname, code) @@ -293,14 +301,8 @@ def generate_source(q, path, version, tag, project): html_code_block = sub('href="#-(\d+)', 'name="L\\1" id="L\\1" href="'+version+'/source'+path+'#L\\1', html_code_block) for f in filters: - c = f['case'] - if (c == 'any' or - (c == 'filename' and filename in f['match']) or - (c == 'extension' and extension in f['match']) or - (c == 'path' and fdir.startswith(tuple(f['match']))) or - (c == 'filename_extension' and filename.endswith(tuple(f['match'])))): - - html_code_block = sub(f ['postrex'], f ['postfunc'], html_code_block) + if filter_applies(f, path): + html_code_block = sub(f['postrex'], f['postfunc'], html_code_block) return html_code_block From dbc71910600121b7620c46798e78d2f63ac0cdab Mon Sep 17 00:00:00 2001 From: Franciszek Stachura Date: Thu, 18 Jul 2024 12:06:21 +0200 Subject: [PATCH 317/529] Change template variable names to be more descriptive --- http/web.py | 25 +++++++++++-------------- templates/ident.html | 4 ++-- templates/layout.html | 12 +++++++++--- templates/sidebar.html | 4 ++-- templates/topbar.html | 18 +++++++++--------- templates/tree.html | 6 +++--- 6 files changed, 36 insertions(+), 33 deletions(-) diff --git a/http/web.py b/http/web.py index f8f9ee62..f7d8cf23 100755 --- a/http/web.py +++ b/http/web.py @@ -406,20 +406,17 @@ def generate_source_page(q, basedir, parsed_path): **template_ctx, 'baseurl': '/' + project + '/', - 'tag': tag, - 'version': version, 'url': url, - 'project': project, - 'projects': get_directories(basedir), - 'ident': '', - 'family': 'A', + + 'current_project': project, + 'current_tag': tag, + 'current_tag_urlencoded': version, 'breadcrumb_links': breadcrumb_links, 'title': title, 'versions': q.query('versions'), - 'url': url, - 'current_tag': tag, + 'projects': get_directories(basedir), } return (status, template.render(data)) @@ -496,19 +493,19 @@ def generate_ident_page(q, basedir, parsed_path): data = { 'baseurl': '/' + project + '/', - 'tag': tag, - 'version': version, 'url': url, - 'project': project, - 'projects': get_directories(basedir), + + 'current_project': project, + 'current_tag': tag, + 'current_tag_urlencoded': version, + 'ident': ident, 'family': family, 'title': ident+' identifier - '+title_suffix, + 'projects': get_directories(basedir), 'versions': q.query('versions'), - 'url': url, - 'current_tag': tag, 'symbol_sections': symbol_sections, } diff --git a/templates/ident.html b/templates/ident.html index 9c6719d0..2c737833 100644 --- a/templates/ident.html +++ b/templates/ident.html @@ -12,7 +12,7 @@

{{ section['title'] }} in {{ symbols|length }} files

- + diff --git a/templates/sidebar.html b/templates/sidebar.html index 3d69d6db..f63a864f 100644 --- a/templates/sidebar.html +++ b/templates/sidebar.html @@ -1,14 +1,14 @@