├── LICENSE ├── README.md ├── android-auto ├── Makefile ├── aap.cpp ├── aap.h ├── custom-gadget ├── default.nix ├── hu.proto ├── main.cpp ├── usbstring.c └── usbstring.h ├── big-endian ├── configuration.nix ├── default.nix └── overlay.nix ├── boottime ├── Makefile └── boottime.c ├── default.nix ├── edid ├── Makefile ├── default.nix └── edid-generate.cpp ├── eeprom-v2 ├── Makefile ├── main.cpp ├── sha256.c └── sha256.h ├── eeprom ├── SpiFs.hs ├── default.nix ├── extractor.hs └── extractor.js ├── elf-tool ├── Makefile ├── default.nix └── elf-tool.c ├── extractor.nix ├── flake.lock ├── flake.nix ├── initrd.nix ├── initrd ├── core.nix └── pi400-keyboard.nix ├── keyboard-proxy └── main.c ├── nix ├── android-auto.nix ├── common.nix ├── keyboard.nix ├── msd.nix ├── pi0-swd.nix ├── sources.json └── sources.nix ├── pi5_voltage.py ├── release.nix ├── repl.nix ├── signing-tool ├── bootcode.js ├── docs.txt └── sign.js ├── smi-speed-overlay.dts ├── smi-test.c ├── uart-speed-overlay.dts ├── utils ├── Makefile ├── arm_divisor.cc ├── common.cpp ├── common.h ├── default.nix ├── drm-utils.cpp ├── drm-utils.h ├── hexdump.cc ├── hexdump.h ├── kms-test.cpp ├── map_peripherals.cpp ├── map_peripherals.h ├── pll-inspector.cpp ├── pv_dumper.cc ├── ramdumper.cc ├── symbol_dump.c └── tearing-test.cpp ├── valgrind.patch └── webusbboot ├── README.md ├── index.html ├── notes.txt ├── pi-usbboot.js ├── ping.mp3 ├── ping.txt └── style.css /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | various tools for working with rpi firmware 2 | 3 | https://github.com/itszor/vc4-toolchain has the full toolchain 4 | 5 | the [Nix package manager](https://nixos.org/nix) also has full support to cross-compile to VC4 and can work on any linux distro 6 | -------------------------------------------------------------------------------- /android-auto/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -Werror 2 | CXXFLAGS += -Wall -Werror 3 | 4 | all: android-auto 5 | 6 | pb/hu.pb.cc pb/hu.pb.h: hu.proto 7 | mkdir -pv pb 8 | protoc $^ --cpp_out=pb/ 9 | 10 | aap.o: aap.cpp pb/hu.pb.h 11 | 12 | android-auto: main.o usbstring.o aap.o pb/hu.pb.o 13 | $(CXX) $^ -o $@ -lpthread -lssl -lcrypto -lprotobuf 14 | #nuke-refs $@ 15 | 16 | install: android-auto 17 | mkdir -pv $(out)/bin 18 | cp $^ -v $(out)/bin 19 | -------------------------------------------------------------------------------- /android-auto/aap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "aap.h" 9 | #include "pb/hu.pb.h" 10 | 11 | static void handleVersionRequest(int fd_in, const uint8_t* buffer, int size); 12 | static void handleSslHandshake(int fd_in, const uint8_t* buffer, int size); 13 | static void sendPacket(int fd_in, uint8_t *buffer, int size, int channel); 14 | 15 | SslConnection::SslConnection(AAConnection *parent) : parent(parent) { 16 | parent->state = handshake; 17 | int ret; 18 | 19 | ERR_load_BIO_strings(); 20 | ERR_load_SSL_strings(); 21 | SSL_library_init(); 22 | OPENSSL_add_all_algorithms_noconf(); 23 | 24 | deviceToHost = BIO_new(BIO_s_mem()); 25 | if (deviceToHost == NULL) { 26 | puts("BIO_new() failed"); 27 | } 28 | hostToDevice = BIO_new(BIO_s_mem()); 29 | if (hostToDevice == NULL) { 30 | puts("BIO_new() failed"); 31 | } 32 | methods = TLS_server_method(); 33 | assert(methods != NULL); 34 | 35 | context = SSL_CTX_new(methods); 36 | assert(context != NULL); 37 | 38 | ret = SSL_CTX_use_certificate_chain_file(context, getenv("AACERT")); 39 | assert(ret == 1); 40 | ret = SSL_CTX_use_PrivateKey_file(context, getenv("AAKEY"), SSL_FILETYPE_PEM); 41 | assert(ret == 1); 42 | 43 | ret = SSL_CTX_check_private_key (context); 44 | assert(ret == 1); 45 | 46 | conn = SSL_new(context); 47 | assert(conn != NULL); 48 | 49 | //SSL_set_msg_callback(conn, SSL_trace); 50 | //SSL_set_msg_callback_arg(conn, BIO_new_fp(stdout,0)); 51 | 52 | SSL_set_bio(conn, hostToDevice, deviceToHost); 53 | BIO_set_write_buf_size(deviceToHost, MAX_FRAME_PAYLOAD_SIZE); 54 | BIO_set_write_buf_size(hostToDevice, MAX_FRAME_PAYLOAD_SIZE); 55 | 56 | puts("accept begin"); 57 | SSL_accept(conn); 58 | puts("acceept end"); 59 | 60 | ret = SSL_do_handshake(conn); 61 | printf("handshake returned %d\n", ret); 62 | } 63 | 64 | void SslConnection::incoming_ciphertext(const uint8_t *buffer, int size) { 65 | int ret = BIO_write(hostToDevice, buffer, size); 66 | assert(ret == size); 67 | if (parent->state == handshake) { 68 | puts("doing handshake step"); 69 | ret = SSL_do_handshake(conn); 70 | printf("done %d\n", ret); 71 | } 72 | } 73 | 74 | uint16_t be16toh16(uint16_t x) { 75 | return ((x & 0xff) << 8) | (x >> 8); 76 | } 77 | 78 | AAConnection aa_conn; // TODO, make it more OO 79 | 80 | void AAConnection::handle_incoming_frame(int fd_in, const uint8_t* buffer, int size) { 81 | uint8_t channel = buffer[0]; 82 | uint8_t flags = buffer[1]; 83 | printf(" channel %d flags %d\n", channel, flags); 84 | if (flags == (HU_FRAME_FIRST_FRAME | HU_FRAME_LAST_FRAME)) { 85 | puts(" frame contains full packet"); 86 | uint16_t frame_size = be16toh16(*((const uint16_t*) (buffer + 2))); 87 | handle_incoming_packet(fd_in, buffer + 4, frame_size); 88 | } else { 89 | puts(" warning, unable to support this flag combination"); 90 | } 91 | } 92 | 93 | void AAConnection::handle_incoming_packet(int fd_in, const uint8_t* buffer, int size) { 94 | HU_INIT_MESSAGE messageCode = (HU_INIT_MESSAGE)be16toh16(*((const uint16_t*)buffer)); 95 | const uint8_t *payload = buffer + 2; 96 | printf("messageCode: %d %d\n", (uint32_t)messageCode, size); 97 | switch (messageCode) { 98 | case HU_INIT_MESSAGE::VersionRequest: 99 | handleVersionRequest(fd_in, payload, size-2); 100 | break; 101 | case HU_INIT_MESSAGE::SSLHandshake: 102 | handleSslHandshake(fd_in, payload, size-2); 103 | break; 104 | case HU_INIT_MESSAGE::AuthComplete: 105 | // payload is a `message AuthCompleteResponse` from the protobuf 106 | // past this point, SSL is online, any frame with HU_FRAME_ENCRYPTED gets shoved into hostToDevice 107 | // SSL_read is then used to pull the plaintext out the other end, size is 1:1 108 | // frames are still re-assembled into packets, as before 109 | handleAuthComplete(payload, size-2); 110 | break; 111 | default: 112 | puts("unhandled message code"); 113 | } 114 | } 115 | 116 | void AAConnection::handleAuthComplete(const void *payload, int size) { 117 | HU::AuthCompleteResponse res; 118 | if (res.ParseFromArray(payload, size)) { 119 | puts("valid protobuf"); 120 | printf("status: %d\n", (uint32_t)res.status()); 121 | } 122 | state = connected; 123 | } 124 | 125 | static void handleVersionRequest(int fd_in, const uint8_t* buffer, int size) { 126 | printf("version request: "); 127 | for (int i=0; i 0) { 163 | uint8_t buffer[pending]; 164 | int ret = BIO_read(deviceToHost, buffer, pending); 165 | assert(ret == pending); 166 | parent->sendUnencBlob(fd_in, AA_CH_CTR, HU_INIT_MESSAGE::SSLHandshake, buffer, pending); 167 | } 168 | } 169 | 170 | void AAConnection::sendUnencBlob(int fd_in, int channel, HU_INIT_MESSAGE messageCode, uint8_t *buffer, int size) { 171 | uint8_t packet[size+2]; 172 | *(uint16_t*)packet = htons((uint16_t)messageCode); 173 | memcpy(packet+2, buffer, size); 174 | sendPacket(fd_in, packet, size+2, channel); 175 | } 176 | -------------------------------------------------------------------------------- /android-auto/aap.h: -------------------------------------------------------------------------------- 1 | // copied from https://github.com/viktorgino/libheadunit 2 | 3 | #include 4 | #include 5 | 6 | enum HU_FRAME_FLAGS 7 | { 8 | HU_FRAME_FIRST_FRAME = 1 << 0, 9 | HU_FRAME_LAST_FRAME = 1 << 1, 10 | HU_FRAME_CONTROL_MESSAGE = 1 << 2, 11 | HU_FRAME_ENCRYPTED = 1 << 3, 12 | }; 13 | 14 | enum class HU_INIT_MESSAGE : uint16_t 15 | { 16 | VersionRequest = 0x0001, 17 | VersionResponse = 0x0002, 18 | SSLHandshake = 0x0003, 19 | AuthComplete = 0x0004, 20 | }; 21 | 22 | enum State { 23 | handshake, 24 | connected 25 | }; 26 | 27 | // Channels ( or Service IDs) 28 | #define AA_CH_CTR 0 // Sync with hu_tra.java, hu_aap.h and hu_aap.c:aa_type_array[] 29 | #define AA_CH_TOU 1 30 | #define AA_CH_SEN 2 31 | #define AA_CH_VID 3 32 | #define AA_CH_AUD 4 33 | #define AA_CH_AU1 5 34 | #define AA_CH_AU2 6 35 | #define AA_CH_MIC 7 36 | #define AA_CH_BT 8 37 | #define AA_CH_PSTAT 9 38 | #define AA_CH_NOT 10 39 | #define AA_CH_NAVI 11 40 | #define AA_CH_MAX 256 41 | 42 | #define MAX_FRAME_PAYLOAD_SIZE 0x4000 43 | 44 | typedef struct { 45 | uint16_t messageCode; 46 | uint8_t payload[6]; 47 | } versionReply; 48 | 49 | class AAConnection; 50 | 51 | class SslConnection { 52 | public: 53 | SslConnection(AAConnection *parent); 54 | BIO *deviceToHost; 55 | BIO *hostToDevice; 56 | void incoming_ciphertext(const uint8_t *buffer, int size); 57 | void maybeSendOutgoingCiphertext(int fd_in); 58 | private: 59 | const SSL_METHOD *methods; 60 | SSL_CTX *context; 61 | SSL *conn; 62 | AAConnection *parent; 63 | }; 64 | 65 | class AAConnection { 66 | public: 67 | AAConnection() : ssl(this) {}; 68 | void sendUnencBlob(int fd_in, int channel, HU_INIT_MESSAGE messageCode, uint8_t *buffer, int size); 69 | void handle_incoming_packet(int fd_in, const uint8_t* buffer, int size); 70 | void handle_incoming_frame(int fd_in, const uint8_t* buffer, int size); 71 | void handleAuthComplete(const void *payload, int size); 72 | 73 | SslConnection ssl; 74 | State state; 75 | }; 76 | 77 | extern AAConnection aa_conn; // TODO, make it more OO 78 | -------------------------------------------------------------------------------- /android-auto/custom-gadget: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "" > /sys/kernel/config/usb_gadget/g1/UDC 4 | 5 | mkdir /dev/gadget 6 | mount -t gadgetfs gadgetfs /dev/gadget 7 | -------------------------------------------------------------------------------- /android-auto/default.nix: -------------------------------------------------------------------------------- 1 | { stdenv, openssl, nukeReferences, protobuf }: 2 | 3 | stdenv.mkDerivation { 4 | name = "android-auto"; 5 | src = ./.; 6 | nativeBuildInputs = [ nukeReferences protobuf ]; 7 | buildInputs = [ openssl protobuf ]; 8 | postFixup = '' 9 | rm $out/nix-support/propagated-build-inputs || true 10 | ''; 11 | enableParallelBuilding = true; 12 | } 13 | -------------------------------------------------------------------------------- /android-auto/main.cpp: -------------------------------------------------------------------------------- 1 | // heavily based on https://blog.soutade.fr/post/2016/07/create-your-own-usb-gadget-with-gadgetfs.html 2 | // and also https://github.com/viktorgino/libheadunit 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "usbstring.h" 24 | #include "aap.h" 25 | 26 | #define FETCH(_var_) \ 27 | memcpy(cp, &_var_, _var_.bLength); \ 28 | cp += _var_.bLength; 29 | 30 | #define CONFIG_VALUE 2 31 | 32 | // Specific to controller 33 | #define USB_DEV "/dev/gadget/fe980000.usb" 34 | #define USB_EPIN "/dev/gadget/ep1in" 35 | #define USB_EPOUT "/dev/gadget/ep4out" 36 | 37 | enum { 38 | STRINGID_MANUFACTURER = 1, 39 | STRINGID_PRODUCT, 40 | STRINGID_SERIAL, 41 | STRINGID_CONFIG_HS, 42 | STRINGID_CONFIG_LS, 43 | STRINGID_INTERFACE, 44 | STRINGID_MAX 45 | }; 46 | 47 | struct io_thread_args { 48 | unsigned stop; 49 | int fd_in, fd_out; 50 | }; 51 | 52 | static struct io_thread_args thread_args; 53 | 54 | static struct usb_string stringtab [] = { 55 | { STRINGID_MANUFACTURER, "https://github.com/cleverca22", }, 56 | { STRINGID_PRODUCT, "android-auto gadget", }, 57 | { STRINGID_SERIAL, "0001", }, 58 | { STRINGID_CONFIG_HS, "High speed configuration", }, 59 | { STRINGID_CONFIG_LS, "Low speed configuration", }, 60 | { STRINGID_INTERFACE, "Android Accessory Interface", }, 61 | { STRINGID_MAX, NULL}, 62 | }; 63 | 64 | static struct usb_gadget_strings strings = { 65 | .language = 0x0409, /* en-us */ 66 | .strings = stringtab, 67 | }; 68 | 69 | 70 | static void* io_thread(void* arg); 71 | 72 | class UsbGadget { 73 | public: 74 | UsbGadget(); 75 | ~UsbGadget(); 76 | int openGadget(); 77 | void populateDeviceDescriptor(struct usb_device_descriptor &dev); 78 | void populateEndpointDescriptors(); 79 | void populateInterfaceDescriptor(struct usb_interface_descriptor &if_descriptor); 80 | int init_ep(int* fd_in, int* fd_out); 81 | void handle_setup_request(int fd, struct usb_ctrlrequest* setup); 82 | void handle_ep0(); 83 | private: 84 | struct usb_endpoint_descriptor ep_descriptor_in; 85 | struct usb_endpoint_descriptor ep_descriptor_out; 86 | int fd; 87 | }; 88 | 89 | UsbGadget::UsbGadget() : fd(-1) { 90 | } 91 | UsbGadget::~UsbGadget() { 92 | if (fd != -1) close(fd); 93 | } 94 | 95 | void UsbGadget::populateDeviceDescriptor(struct usb_device_descriptor &device_descriptor) { 96 | device_descriptor.bLength = USB_DT_DEVICE_SIZE; 97 | device_descriptor.bDescriptorType = USB_DT_DEVICE; 98 | device_descriptor.bDeviceClass = 0; 99 | device_descriptor.bDeviceSubClass = 0; 100 | device_descriptor.bDeviceProtocol = 0; 101 | //device_descriptor.bMaxPacketSize0 = 255; Set by driver 102 | device_descriptor.idVendor = 0x18D1; 103 | device_descriptor.idProduct = 0x2D00; 104 | device_descriptor.bcdDevice = 0xffff; // Version 105 | // Strings 106 | device_descriptor.iManufacturer = STRINGID_MANUFACTURER; 107 | device_descriptor.iProduct = STRINGID_PRODUCT; 108 | device_descriptor.iSerialNumber = STRINGID_SERIAL; 109 | device_descriptor.bNumConfigurations = 1; // Only one configuration 110 | } 111 | 112 | void UsbGadget::populateEndpointDescriptors() { 113 | ep_descriptor_in.bLength = USB_DT_ENDPOINT_SIZE; 114 | ep_descriptor_in.bDescriptorType = USB_DT_ENDPOINT; 115 | ep_descriptor_in.bEndpointAddress = USB_DIR_IN | 1; 116 | ep_descriptor_in.bmAttributes = USB_ENDPOINT_XFER_BULK; 117 | ep_descriptor_in.wMaxPacketSize = 512; // HS size 118 | 119 | ep_descriptor_out.bLength = USB_DT_ENDPOINT_SIZE; 120 | ep_descriptor_out.bDescriptorType = USB_DT_ENDPOINT; 121 | ep_descriptor_out.bEndpointAddress = USB_DIR_OUT | 4; 122 | ep_descriptor_out.bmAttributes = USB_ENDPOINT_XFER_BULK; 123 | ep_descriptor_out.wMaxPacketSize = 512; // HS size 124 | } 125 | 126 | void UsbGadget::populateInterfaceDescriptor(struct usb_interface_descriptor &if_descriptor) { 127 | if_descriptor.bLength = sizeof(if_descriptor); 128 | if_descriptor.bDescriptorType = USB_DT_INTERFACE; 129 | if_descriptor.bInterfaceNumber = 0; 130 | if_descriptor.bAlternateSetting = 0; 131 | if_descriptor.bNumEndpoints = 2; 132 | if_descriptor.bInterfaceClass = 255; 133 | if_descriptor.bInterfaceSubClass = 255; 134 | if_descriptor.bInterfaceProtocol = 0; 135 | if_descriptor.iInterface = STRINGID_INTERFACE; 136 | } 137 | 138 | int UsbGadget::openGadget() { 139 | uint8_t init_config[2048]; 140 | uint8_t* cp; 141 | struct usb_device_descriptor device_descriptor; 142 | struct usb_interface_descriptor if_descriptor; 143 | struct usb_config_descriptor config; 144 | struct usb_config_descriptor config_hs; 145 | uint32_t send_size; 146 | uint32_t ret; 147 | 148 | fd = open(USB_DEV, O_RDWR|O_SYNC); 149 | 150 | if (fd <= 0) { 151 | printf("Unable to open %s (%m)\n", USB_DEV); 152 | return 1; 153 | } 154 | 155 | *(uint32_t*)init_config = 0; 156 | cp = &init_config[4]; 157 | populateDeviceDescriptor(device_descriptor); 158 | populateEndpointDescriptors(); 159 | populateInterfaceDescriptor(if_descriptor); 160 | 161 | config_hs.bLength = sizeof(config_hs); 162 | config_hs.bDescriptorType = USB_DT_CONFIG; 163 | config_hs.wTotalLength = config_hs.bLength + 164 | if_descriptor.bLength + ep_descriptor_in.bLength + ep_descriptor_out.bLength; 165 | config_hs.bNumInterfaces = 1; 166 | config_hs.bConfigurationValue = CONFIG_VALUE; 167 | config_hs.iConfiguration = STRINGID_CONFIG_HS; 168 | config_hs.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; 169 | config_hs.bMaxPower = 1; 170 | 171 | config.bLength = sizeof(config); 172 | config.bDescriptorType = USB_DT_CONFIG; 173 | config.wTotalLength = config.bLength + 174 | if_descriptor.bLength + ep_descriptor_in.bLength + ep_descriptor_out.bLength; 175 | config.bNumInterfaces = 1; 176 | config.bConfigurationValue = CONFIG_VALUE; 177 | config.iConfiguration = STRINGID_CONFIG_LS; 178 | config.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; 179 | config.bMaxPower = 1; 180 | 181 | FETCH(config); 182 | FETCH(if_descriptor); 183 | FETCH(ep_descriptor_in); 184 | FETCH(ep_descriptor_out); 185 | 186 | FETCH(config_hs); 187 | FETCH(if_descriptor); 188 | FETCH(ep_descriptor_in); 189 | FETCH(ep_descriptor_out); 190 | 191 | FETCH(device_descriptor); 192 | 193 | // Configure ep0 194 | send_size = (uint32_t)cp-(uint32_t)init_config; 195 | ret = write(fd, init_config, send_size); 196 | 197 | if (ret != send_size) { 198 | printf("Write error %d (%m)\n", ret); 199 | return 2; 200 | } 201 | printf("ep0 configured %d\n", send_size); 202 | return 0; 203 | } 204 | 205 | int main() 206 | { 207 | UsbGadget gadget; 208 | gadget.openGadget(); 209 | 210 | gadget.handle_ep0(); 211 | 212 | return 0; 213 | } 214 | 215 | void UsbGadget::handle_ep0() { 216 | int ret, nevents, i; 217 | fd_set read_set; 218 | struct usb_gadgetfs_event events[5]; 219 | 220 | while (1) { 221 | printf("handle_ep0 %d\n", fd); 222 | FD_ZERO(&read_set); 223 | FD_SET(fd, &read_set); 224 | 225 | ret = select(fd+1, &read_set, NULL, NULL, NULL); 226 | printf("ep0 select returned %d\n", ret); 227 | if (FD_ISSET(fd, &read_set)) { 228 | ret = read(fd, &events, sizeof(events)); 229 | 230 | if (ret < 0) { 231 | printf("ep0 Read error %d (%m)\n", ret); 232 | break; 233 | } 234 | 235 | nevents = ret / sizeof(events[0]); 236 | 237 | printf("%d event(s)\n", nevents); 238 | 239 | for (i=0; ibRequest); 266 | 267 | switch (setup->bRequest) { 268 | case USB_REQ_GET_DESCRIPTOR: 269 | if (setup->bRequestType != USB_DIR_IN) goto stall; 270 | switch (setup->wValue >> 8) { 271 | case USB_DT_STRING: 272 | printf(" Get string id #%d (max length %d)\n", setup->wValue & 0xff, setup->wLength); 273 | status = usb_gadget_get_string (&strings, setup->wValue & 0xff, buffer); 274 | // Error 275 | if (status < 0) { 276 | printf("String not found !!\n"); 277 | break; 278 | } else { 279 | printf(" Found %d bytes\n", status); 280 | } 281 | ret = write(fd, buffer, status); 282 | assert(ret == status); 283 | return; 284 | default: 285 | printf("Cannot return descriptor %d\n", (setup->wValue >> 8)); 286 | } 287 | break; 288 | case USB_REQ_SET_CONFIGURATION: 289 | if (setup->bRequestType != USB_DIR_OUT) { 290 | printf("Bad dir\n"); 291 | goto stall; 292 | } 293 | switch (setup->wValue) { 294 | case CONFIG_VALUE: 295 | printf("Set config value\n"); 296 | if (!thread_args.stop) { 297 | thread_args.stop = 1; 298 | usleep(200000); // Wait for termination 299 | } 300 | if (thread_args.fd_in <= 0) { 301 | status = init_ep(&thread_args.fd_in, &thread_args.fd_out); 302 | } else { 303 | status = 0; 304 | } 305 | if (!status) { 306 | thread_args.stop = 0; 307 | pthread_create(&thread, NULL, io_thread, &thread_args); 308 | } 309 | break; 310 | case 0: 311 | printf("Disable threads\n"); 312 | thread_args.stop = 1; 313 | break; 314 | default: 315 | printf("Unhandled configuration value %d\n", setup->wValue); 316 | break; 317 | } 318 | // Just ACK 319 | status = read(fd, &status, 0); 320 | return; 321 | case USB_REQ_GET_INTERFACE: 322 | printf("GET_INTERFACE\n"); 323 | buffer[0] = 0; 324 | ret = write (fd, buffer, 1); 325 | assert(ret == 1); 326 | return; 327 | case USB_REQ_SET_INTERFACE: 328 | printf("SET_INTERFACE\n"); 329 | ioctl(thread_args.fd_in, GADGETFS_CLEAR_HALT); 330 | ioctl(thread_args.fd_out, GADGETFS_CLEAR_HALT); 331 | // ACK 332 | status = read(fd, &status, 0); 333 | return; 334 | } 335 | 336 | stall: 337 | printf("Stalled\n"); 338 | // Error 339 | if (setup->bRequestType & USB_DIR_IN) { 340 | ret = read (fd, &status, 0); 341 | assert(ret == 0); 342 | } else { 343 | ret = write (fd, &status, 0); 344 | assert(ret == 0); 345 | } 346 | } 347 | 348 | int UsbGadget::init_ep(int* fd_in, int* fd_out) { 349 | uint8_t init_config[2048]; 350 | uint8_t* cp; 351 | uint32_t ret = -1; 352 | uint32_t send_size; 353 | 354 | // Configure ep1 (low/full speed + high speed) 355 | *fd_in = open(USB_EPIN, O_RDWR); 356 | 357 | if (*fd_in <= 0) { 358 | printf("Unable to open %s (%m)\n", USB_EPIN); 359 | goto end; 360 | } 361 | 362 | *(uint32_t*)init_config = 1; 363 | cp = &init_config[4]; 364 | 365 | FETCH(ep_descriptor_in); 366 | FETCH(ep_descriptor_in); 367 | 368 | send_size = (uint32_t)cp-(uint32_t)init_config; 369 | ret = write(*fd_in, init_config, send_size); 370 | 371 | if (ret != send_size) { 372 | printf("Write error %d (%m)\n", ret); 373 | goto end; 374 | } 375 | 376 | puts("ep1 configured"); 377 | 378 | // Configure ep4 (low/full speed + high speed) 379 | *fd_out = open(USB_EPOUT, O_RDWR); 380 | 381 | if (*fd_out <= 0) { 382 | printf("Unable to open %s (%m)\n", USB_EPOUT); 383 | goto end; 384 | } 385 | 386 | *(uint32_t*)init_config = 1; 387 | cp = &init_config[4]; 388 | 389 | FETCH(ep_descriptor_out); 390 | FETCH(ep_descriptor_out); 391 | 392 | send_size = (uint32_t)cp-(uint32_t)init_config; 393 | ret = write(*fd_out, init_config, send_size); 394 | 395 | if (ret != send_size) { 396 | printf("Write error %d (%m)\n", ret); 397 | goto end; 398 | } 399 | 400 | puts("ep4 configured"); 401 | 402 | ret = 0; 403 | 404 | end: 405 | return ret; 406 | } 407 | 408 | /* 409 | * Respond to host requests 410 | */ 411 | static void* io_thread(void* arg) { 412 | struct io_thread_args* thread_args = (struct io_thread_args*)arg; 413 | fd_set read_set; 414 | struct timeval timeout; 415 | int ret, max_read_fd, max_write_fd; 416 | char buffer[512]; 417 | 418 | max_read_fd = max_write_fd = 0; 419 | 420 | if (thread_args->fd_in > max_write_fd) max_write_fd = thread_args->fd_in; 421 | if (thread_args->fd_out > max_read_fd) max_read_fd = thread_args->fd_out; 422 | 423 | while (!thread_args->stop) { 424 | FD_ZERO(&read_set); 425 | FD_SET(thread_args->fd_out, &read_set); 426 | timeout.tv_sec = 0; 427 | timeout.tv_usec = 10000; // 10ms 428 | 429 | memset(buffer, 0, sizeof(buffer)); 430 | ret = select(max_read_fd+1, &read_set, NULL, NULL, &timeout); 431 | printf("ep4 select returned %d\n", ret); 432 | 433 | // Timeout 434 | if (ret == 0) continue; 435 | 436 | // Error 437 | if (ret < 0) break; 438 | 439 | if (FD_ISSET(thread_args->fd_out, &read_set)) { 440 | ret = read (thread_args->fd_out, buffer, sizeof(buffer)); 441 | 442 | if (ret > 0) { 443 | printf(" Read %d bytes : %s\n", ret, buffer); 444 | aa_conn.handle_incoming_frame(thread_args->fd_in, (uint8_t*)buffer, ret); 445 | } else { 446 | printf(" Read error %d(%m)\n", ret); 447 | } 448 | 449 | //FD_ZERO(&write_set); 450 | //FD_SET(thread_args->fd_in, &write_set); 451 | 452 | //memset(buffer, 0, sizeof(buffer)); 453 | //ret = select(max_write_fd+1, NULL, &write_set, NULL, NULL); 454 | 455 | // Error 456 | //if (ret < 0) 457 | // break; 458 | 459 | //strcpy(buffer, "My name is USBond !"); 460 | 461 | //ret = write (thread_args->fd_in, buffer, strlen(buffer)+1); 462 | 463 | //printf("Write status %d (%m)\n", ret); 464 | } 465 | aa_conn.ssl.maybeSendOutgoingCiphertext(thread_args->fd_in); 466 | } 467 | 468 | close(thread_args->fd_in); 469 | close(thread_args->fd_out); 470 | 471 | thread_args->fd_in = -1; 472 | thread_args->fd_out = -1; 473 | 474 | return NULL; 475 | } 476 | 477 | -------------------------------------------------------------------------------- /android-auto/usbstring.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 David Brownell 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published 6 | * by the Free Software Foundation; either version 2.1 of the License, or 7 | * (at your option) any later version. 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "usbstring.h" 17 | 18 | static inline void put_unaligned_le16(__u16 val, __u16 *cp) 19 | { 20 | __u8 *p = (void *)cp; 21 | 22 | *p++ = (__u8) val; 23 | *p++ = (__u8) (val >> 8); 24 | } 25 | 26 | static int utf8_to_utf16le(const char *s, __u16 *cp, unsigned len) 27 | { 28 | int count = 0; 29 | __u8 c; 30 | __u16 uchar; 31 | 32 | /* this insists on correct encodings, though not minimal ones. 33 | * BUT it currently rejects legit 4-byte UTF-8 code points, 34 | * which need surrogate pairs. (Unicode 3.1 can use them.) 35 | */ 36 | while (len != 0 && (c = (__u8) *s++) != 0) { 37 | if (c & 0x80) { 38 | // 2-byte sequence: 39 | // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx 40 | if ((c & 0xe0) == 0xc0) { 41 | uchar = (c & 0x1f) << 6; 42 | 43 | c = (__u8) *s++; 44 | if ((c & 0xc0) != 0xc0) 45 | goto fail; 46 | c &= 0x3f; 47 | uchar |= c; 48 | 49 | // 3-byte sequence (most CJKV characters): 50 | // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx 51 | } else if ((c & 0xf0) == 0xe0) { 52 | uchar = (c & 0x0f) << 12; 53 | 54 | c = (__u8) *s++; 55 | if ((c & 0xc0) != 0xc0) 56 | goto fail; 57 | c &= 0x3f; 58 | uchar |= c << 6; 59 | 60 | c = (__u8) *s++; 61 | if ((c & 0xc0) != 0xc0) 62 | goto fail; 63 | c &= 0x3f; 64 | uchar |= c; 65 | 66 | /* no bogus surrogates */ 67 | if (0xd800 <= uchar && uchar <= 0xdfff) 68 | goto fail; 69 | 70 | // 4-byte sequence (surrogate pairs, currently rare): 71 | // 11101110wwwwzzzzyy + 110111yyyyxxxxxx 72 | // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 73 | // (uuuuu = wwww + 1) 74 | // FIXME accept the surrogate code points (only) 75 | 76 | } else 77 | goto fail; 78 | } else 79 | uchar = c; 80 | put_unaligned_le16 (uchar, cp++); 81 | count++; 82 | len--; 83 | } 84 | return count; 85 | fail: 86 | return -1; 87 | } 88 | 89 | 90 | /** 91 | * usb_gadget_get_string - fill out a string descriptor 92 | * @table: of c strings encoded using UTF-8 93 | * @id: string id, from low byte of wValue in get string descriptor 94 | * @buf: at least 256 bytes 95 | * 96 | * Finds the UTF-8 string matching the ID, and converts it into a 97 | * string descriptor in utf16-le. 98 | * Returns length of descriptor (always even) or negative errno 99 | * 100 | * If your driver needs strings in multiple languages, you'll probably 101 | * "switch (wIndex) { ... }" in your ep0 string descriptor logic, 102 | * using this routine after choosing which set of UTF-8 strings to use. 103 | * 104 | * Note that US-ASCII is a strict subset of UTF-8; any string bytes with 105 | * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 106 | * characters. 107 | */ 108 | int 109 | usb_gadget_get_string (struct usb_gadget_strings *table, int id, __u8 *buf) 110 | { 111 | struct usb_string *s; 112 | int len; 113 | 114 | /* descriptor 0 has the language id */ 115 | if (id == 0) { 116 | buf [0] = 4; 117 | buf [1] = USB_DT_STRING; 118 | buf [2] = (__u8) table->language; 119 | buf [3] = (__u8) (table->language >> 8); 120 | return 4; 121 | } 122 | for (s = table->strings; s && s->s; s++) 123 | if (s->id == id) 124 | break; 125 | 126 | /* unrecognized: stall. */ 127 | if (!s || !s->s) 128 | return -EINVAL; 129 | 130 | /* string descriptors have length, tag, then UTF16-LE text */ 131 | len = strlen (s->s); 132 | if (len > 126) 133 | len = 126; 134 | memset (buf + 2, 0, 2 * len); /* zero all the bytes */ 135 | len = utf8_to_utf16le(s->s, (__u16 *)&buf[2], len); 136 | if (len < 0) 137 | return -EINVAL; 138 | buf [0] = (len + 1) * 2; 139 | buf [1] = USB_DT_STRING; 140 | return buf [0]; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /android-auto/usbstring.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (c) Copyright 2003 by David Brownell 3 | * All Rights Reserved. 4 | * 5 | * This software is licensed under the GNU LGPL version 2. 6 | */ 7 | 8 | /* utility to simplify dealing with string descriptors */ 9 | 10 | /** 11 | * struct usb_string - wraps a C string and its USB id 12 | * @id: the (nonzero) ID for this string 13 | * @s: the string, in UTF-8 encoding 14 | * 15 | * If you're using usb_gadget_get_string(), use this to wrap a string 16 | * together with its ID. 17 | */ 18 | struct usb_string { 19 | __u8 id; 20 | const char *s; 21 | }; 22 | 23 | /** 24 | * struct usb_gadget_strings - a set of USB strings in a given language 25 | * @language: identifies the strings' language (0x0409 for en-us) 26 | * @strings: array of strings with their ids 27 | * 28 | * If you're using usb_gadget_get_string(), use this to wrap all the 29 | * strings for a given language. 30 | */ 31 | struct usb_gadget_strings { 32 | __u16 language; /* 0x0409 for en-us */ 33 | struct usb_string *strings; 34 | }; 35 | 36 | #ifdef __cplusplus 37 | extern "C"{ 38 | #endif 39 | 40 | /* put descriptor for string with that id into buf (buflen >= 256) */ 41 | int usb_gadget_get_string (struct usb_gadget_strings *table, int id, __u8 *buf); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | -------------------------------------------------------------------------------- /big-endian/configuration.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, ... }: 2 | 3 | let 4 | patchedKernel = pkgs.linux_rpi3.override { 5 | structuredExtraConfig = with lib.kernel; { 6 | BLK_MQ_VIRTIO = yes; 7 | CPU_BIG_ENDIAN = yes; 8 | CRYPTO_AEGIS128_SIMD = no; 9 | PCI = yes; 10 | PCI_HOST_GENERIC = yes; 11 | SCSI_VIRTIO = yes; 12 | VIRTIO = yes; 13 | VIRTIO_BLK = yes; 14 | VIRTIO_MENU = yes; 15 | VIRTIO_MMIO = yes; 16 | VIRTIO_NET = yes; 17 | VIRTIO_PCI = yes; 18 | VIRTIO_PCI_LEGACY = yes; 19 | }; 20 | }; 21 | patchedKernelPackages = pkgs.linuxPackagesFor patchedKernel; 22 | in { 23 | nixpkgs.crossSystem.system = "aarch64_be-linux"; 24 | boot = { 25 | kernelPackages = patchedKernelPackages; 26 | isContainer = false; 27 | consoleLogLevel = 8; 28 | }; 29 | boot.loader.grub.enable = false; 30 | fileSystems = { 31 | "/" = { 32 | label = "nixos"; 33 | }; 34 | }; 35 | nixpkgs.overlays = [ (import ./overlay.nix) ]; 36 | security.polkit.enable = false; 37 | services.udisks2.enable = false; 38 | users.users.root = { 39 | initialPassword = "root"; 40 | }; 41 | services.openssh.enable = true; 42 | services.openssh.permitRootLogin = "yes"; 43 | } 44 | -------------------------------------------------------------------------------- /big-endian/default.nix: -------------------------------------------------------------------------------- 1 | { nixpkgs-be }: 2 | 3 | let 4 | pkgs = import nixpkgs-be { system = "x86_64-linux"; }; 5 | hello = pkgs.pkgsCross.aarch64be-multiplatform.hello; 6 | oldinitrd = pkgs.makeInitrd { 7 | contents = [ 8 | { 9 | symlink = "/init"; 10 | object = "${hello}/bin/hello"; 11 | } 12 | ]; 13 | }; 14 | eval = import (nixpkgs-be + "/nixos") { system = "x86_64-linux"; configuration = ./configuration.nix; }; 15 | diskImage = pkgs.callPackage (nixpkgs-be + "/nixos/lib/make-ext4-fs.nix") { 16 | storePaths = [ eval.system ]; 17 | volumeLabel = "nixos"; 18 | }; 19 | kernel = "${eval.config.system.build.kernel}/${eval.config.system.boot.loader.kernelFile}"; 20 | initrd = "${eval.config.system.build.initialRamdisk}/initrd"; 21 | script = (pkgs.writeShellScript "runner" '' 22 | export PATH=${pkgs.lib.makeBinPath [ pkgs.qemu ]} 23 | qemu-system-aarch64 -kernel ${kernel} \ 24 | -append '${builtins.unsafeDiscardStringContext (toString (eval.config.boot.kernelParams))} root=/dev/vda init=${builtins.unsafeDiscardStringContext eval.config.system.build.toplevel}/init console=ttyAMA0' \ 25 | -initrd ${initrd} \ 26 | -machine virt -serial stdio -cpu cortex-a72 \ 27 | -drive id=rootfs,if=virtio,snapshot=on,format=raw,file=${diskImage} \ 28 | -nic user,hostfwd=tcp:127.0.0.2:2222-:22,model=virtio-net-pci \ 29 | -m 1024 30 | '') // { inherit eval; }; 31 | in { 32 | inherit script diskImage eval; 33 | } 34 | -------------------------------------------------------------------------------- /big-endian/overlay.nix: -------------------------------------------------------------------------------- 1 | self: super: { 2 | systemd = super.systemd.override { withEfi = false; }; 3 | makeModulesClosure = args: super.makeModulesClosure (args // { 4 | allowMissing = true; 5 | }); 6 | } 7 | -------------------------------------------------------------------------------- /boottime/Makefile: -------------------------------------------------------------------------------- 1 | boottime: boottime.c 2 | gcc $< -o $@ -lbcm_host -Wall 3 | -------------------------------------------------------------------------------- /boottime/boottime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char **argv) { 11 | int fd = open("/dev/mem", O_RDONLY); 12 | if (fd == -1) { 13 | perror("unable to open /dev/mem"); 14 | return 2; 15 | } 16 | uint64_t physaddr; 17 | // physaddr = 0x20000000; // bcm2835 18 | // physaddr = 0xfe000000; // bcm2711 19 | physaddr = 0x1000000000; // bcm2712 20 | //physaddr = bcm_host_get_peripheral_address(); 21 | //printf("addr %lx\n", physaddr); 22 | void *addr = mmap(NULL, 16 * 1024 * 1024, PROT_READ, MAP_SHARED, fd, physaddr); 23 | if (addr == MAP_FAILED) { 24 | perror("unable to mmap"); 25 | return 1; 26 | } 27 | volatile uint32_t *st_clo = (volatile uint32_t*)(addr + 0x3004); 28 | uint32_t snapshot = *st_clo; 29 | munmap(addr, 16 * 1024 * 1024); 30 | close(fd); 31 | fd = open("/proc/uptime", O_RDONLY); 32 | char buffer[1024]; 33 | int size = read(fd, buffer, 1024); 34 | close(fd); 35 | double soc_uptime = ((float)snapshot / 1000000); 36 | double linux_uptime = atof(buffer); 37 | printf("uptimes soc:%lf - linux:%lf == %lf\n", soc_uptime, linux_uptime, soc_uptime - linux_uptime); 38 | return 0; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | let 2 | sources = import nix/sources.nix; 3 | common = import ./nix/common.nix; 4 | msd = import ./nix/msd.nix; 5 | keyboard = import ./nix/keyboard.nix; 6 | android-auto = import ./nix/android-auto.nix; 7 | pkgs = import sources.nixpkgs { 8 | overlays = [ common msd android-auto keyboard ]; 9 | }; 10 | # nix build -L -f ~/apps/rpi/rpi-tools/ pkgsCross.armv7l-hf-multiplatform.pkgsStatic.msd.rootDir 11 | # nix build -L -f ~/apps/rpi/rpi-tools/ pkgsCross.armv7l-hf-multiplatform.android-auto.rootDir 12 | in pkgs 13 | -------------------------------------------------------------------------------- /edid/Makefile: -------------------------------------------------------------------------------- 1 | edid-generate: edid-generate.cpp 2 | $(CXX) -o $@ $^ 3 | 4 | install: edid-generate 5 | mkdir -pv $(out)/bin 6 | cp $^ $(out)/bin/ 7 | -------------------------------------------------------------------------------- /edid/default.nix: -------------------------------------------------------------------------------- 1 | { stdenv }: 2 | 3 | stdenv.mkDerivation { 4 | name = "edid"; 5 | src = ./.; 6 | } 7 | -------------------------------------------------------------------------------- /edid/edid-generate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void add_detailed(uint8_t *buf, uint32_t pixel_clock, 11 | uint16_t hactive, uint16_t hfp, uint16_t hsync, uint16_t hbp, 12 | uint16_t vactive, uint16_t vfp, uint16_t vsync, uint8_t vbp, 13 | bool interlaced) { 14 | uint16_t hblank = hfp + hsync + hbp; 15 | uint16_t vblank = vfp + vsync + vbp; 16 | 17 | uint32_t htotal = hactive + hblank; 18 | uint32_t vtotal = vactive + vblank; 19 | uint32_t total = vtotal * htotal; 20 | printf("hfreq: %f, vfreq: %f\n", (float)pixel_clock / (float)htotal, (float)pixel_clock / (float)total); 21 | printf("htotal: %d, vtotal: %d\n", htotal, vtotal); 22 | printf("hblank: %d, hblank: %d\n", hblank, vblank); 23 | 24 | pixel_clock = pixel_clock / (10 * 1000); 25 | buf[0] = pixel_clock & 0xff; 26 | buf[1] = pixel_clock >> 8; 27 | 28 | buf[2] = hactive & 0xff; 29 | 30 | buf[3] = hblank & 0xff;; 31 | 32 | buf[4] = (((hactive >> 8) & 0xf)<<4) | ((hblank >> 8) & 0xf); 33 | 34 | buf[5] = vactive & 0xff; 35 | 36 | buf[6] = vblank & 0xff; 37 | 38 | buf[7] = (((vactive >> 8) & 0xf) << 4) | ((vblank >> 8) & 0xf); 39 | buf[8] = hfp & 0xff; 40 | buf[9] = hsync & 0xff; 41 | buf[10] = ((vfp & 0xf) << 4) | (vsync & 0xf); 42 | buf[11] = (((hfp>>8)&3) << 6) | (((hsync>>8)&3) << 4) | (((vfp>>8)&3)<<2) | ((vsync>>8)&2); 43 | buf[12] = 40; 44 | buf[13] = 30; 45 | 46 | buf[17] = (interlaced << 7) | (3 << 3); 47 | } 48 | 49 | int main(int argc, char **argv) { 50 | uint8_t buffer[128]; 51 | memset(buffer, 0, 128); 52 | 53 | buffer[0] = 0; 54 | buffer[1] = 0xff; 55 | buffer[2] = 0xff; 56 | buffer[3] = 0xff; 57 | buffer[4] = 0xff; 58 | buffer[5] = 0xff; 59 | buffer[6] = 0xff; 60 | buffer[7] = 0; 61 | 62 | buffer[8] = 0; 63 | buffer[9] = 0; 64 | 65 | buffer[10] = 0; 66 | buffer[11] = 0; 67 | 68 | buffer[12] = 0; 69 | buffer[13] = 0; 70 | buffer[14] = 0; 71 | buffer[15] = 0; 72 | 73 | buffer[16] = 0xff; 74 | 75 | buffer[17] = (2024 - 1990); 76 | 77 | buffer[18] = 1; 78 | buffer[19] = 4; 79 | 80 | buffer[20] = (1<<7) | (2 << 4) | 1; 81 | 82 | for (int i=0; i<8; i++) { 83 | buffer[38+(i*2)] = 1; 84 | buffer[38+(i*2)+1] = 1; 85 | } 86 | 87 | add_detailed(buffer + 54 + (18 * 0), 88 | 27 * 1000 * 1000, // pclk 89 | 1440, 40, 118, 118, // hactive/hfp/hsync/hbp 90 | 240, 3, 4, 15, // vactive/vfp/vsync/vbp 91 | true); 92 | 93 | uint8_t sum = 0; 94 | for (int i=0; i<128; i++) { 95 | sum += buffer[i]; 96 | } 97 | buffer[127] = 256 - sum; 98 | 99 | int fd = open("test.bin", O_WRONLY | O_CREAT, 0755); 100 | assert(fd >= 0); 101 | int ret = write(fd, buffer, 128); 102 | assert(ret == 128); 103 | close(fd); 104 | } 105 | -------------------------------------------------------------------------------- /eeprom-v2/Makefile: -------------------------------------------------------------------------------- 1 | eeprom-util: main.cpp sha256.c 2 | $(CXX) -o $@ $^ -Wall 3 | 4 | test: eeprom-util 5 | ./eeprom-util -p ~/apps/rpi/rpi-eeprom/firmware-2712/default/pieeprom-2024-11-12.bin -o out 6 | 7 | .PHONY: test 8 | -------------------------------------------------------------------------------- /eeprom-v2/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // hashing code from https://github.com/B-Con/crypto-algorithms 11 | #include "sha256.h" 12 | 13 | using namespace std; 14 | 15 | #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) 16 | 17 | // decompression code based on https://git.venev.name/hristo/rpi-eeprom-compress.git 18 | int uncompress(const uint8_t *data, int size, uint8_t **output) { 19 | //const uint8_t *start = data; 20 | uint8_t *uncompressed = (uint8_t*)malloc(1024*1024*16); 21 | uint8_t *outptr = uncompressed; 22 | 23 | const uint8_t *end = data + size; 24 | uint8_t out_i = 0; 25 | char outbuf[256]; 26 | while (data < end) { 27 | uint8_t c = *data++; 28 | uint8_t cmd = c; 29 | //printf("offset 0x%x, cmd 0x%x\n", data-start, cmd); 30 | for (int i=0; i<8; i++) { 31 | if (cmd & 1) { 32 | uint8_t offset = (*data++) + 1; 33 | uint8_t len = *data++; 34 | do { 35 | c = outbuf[(uint8_t)(out_i - offset)]; 36 | outbuf[out_i++] = c; 37 | *outptr++ = c; 38 | } while(len--); 39 | } else { 40 | if (data >= end) break; 41 | c = *data++; 42 | outbuf[out_i++] = c; 43 | *outptr++ = c; 44 | } 45 | cmd >>= 1; 46 | } 47 | } 48 | *output = uncompressed; 49 | return outptr - uncompressed; 50 | } 51 | 52 | void handle_chunk(uint32_t magic, const uint8_t *data, int size, string output_path) { 53 | switch (magic) { 54 | case 0x55aaf00f: 55 | { 56 | printf("%d byte stage1\n", size); 57 | string out = output_path + "/bootcode.bin"; 58 | 59 | FILE *fp = fopen(out.c_str(), "wb"); 60 | fwrite(data, size, 1, fp); 61 | fclose(fp); 62 | break; 63 | } 64 | case 0x55aaf11f: 65 | case 0x55aaf22f: 66 | { 67 | const char *name = (const char*)data; 68 | printf("%d byte plain file, \"%s\"\n", size, name); 69 | string out = output_path + "/" + name; 70 | 71 | FILE *fp = fopen(out.c_str(), "wb"); 72 | fwrite(data+16, size-16, 1, fp); 73 | fclose(fp); 74 | break; 75 | } 76 | case 0x55aaf33f: 77 | { 78 | uint8_t *uncompressed; 79 | const char *name = (const char*)data; 80 | const uint8_t *hash = data + (size - 32); 81 | printf("%d byte compressed file, \"%s\"\n", size, name); 82 | int orig_size = uncompress(data + 16, size - 16 - 32, &uncompressed); 83 | printf("%d\n", orig_size); 84 | 85 | SHA256_CTX ctx; 86 | 87 | sha256_init(&ctx); 88 | sha256_update(&ctx, uncompressed, orig_size); 89 | uint8_t actual_hash[32]; 90 | sha256_final(&ctx, actual_hash); 91 | 92 | if (memcmp(hash, actual_hash, 32) == 0) { 93 | puts("hash match"); 94 | string out = output_path + "/" + name; 95 | FILE *fp = fopen(out.c_str(), "wb"); 96 | fwrite(uncompressed, orig_size, 1, fp); 97 | fclose(fp); 98 | free(uncompressed); 99 | } else { 100 | string out = output_path + "/" + name; 101 | string hashname = out + ".hash"; 102 | 103 | FILE *fp = fopen(hashname.c_str(), "wb"); 104 | fwrite(hash, 32, 1, fp); 105 | fclose(fp); 106 | 107 | string rawname = out + ".raw"; 108 | 109 | fp = fopen(rawname.c_str(), "wb"); 110 | fwrite(data + 16, size - 16 - 32, 1, fp); 111 | fclose(fp); 112 | } 113 | break; 114 | } 115 | case 0x55aafeef: 116 | //printf("%d byte alignment padding\n", size); 117 | break; 118 | default: 119 | printf("unhandled magic 0x%x\n", magic); 120 | } 121 | } 122 | 123 | void unpack_eeprom(const uint8_t *bin, int size, const char *output_path) { 124 | int offset = 0; 125 | while (offset < size) { 126 | const uint32_t *ptr = (const uint32_t*)(bin + offset); 127 | uint32_t magic = ntohl(ptr[0]); 128 | uint32_t chunk_size = ntohl(ptr[1]); 129 | if (magic == 0xffffffff) break; 130 | printf("%x %x, offset 0x%x\n", magic, chunk_size, offset+8); 131 | 132 | const uint8_t *chunk = bin + offset + 8; 133 | handle_chunk(magic, chunk, chunk_size, output_path); 134 | offset = ROUNDUP(offset + 8 + chunk_size, 8); 135 | } 136 | } 137 | 138 | int main(int argc, char **argv) { 139 | int opt; 140 | const char *input_path = NULL; 141 | const char *output_path = NULL; 142 | while ((opt = getopt(argc, argv, "p:o:")) != -1) { 143 | switch (opt) { 144 | case 'p': 145 | input_path = optarg; 146 | break; 147 | case 'o': 148 | output_path = optarg; 149 | break; 150 | } 151 | } 152 | if (!input_path) { 153 | fprintf(stderr, "error, no input specified, please use %s -p \n", argv[0]); 154 | return 1; 155 | } 156 | 157 | FILE *eeprom = fopen(input_path, "rb"); 158 | fseek(eeprom, 0L, SEEK_END); 159 | int size = ftell(eeprom); 160 | fseek(eeprom, 0, SEEK_SET); 161 | 162 | void *raw_file = malloc(size); 163 | int res = fread(raw_file, size, 1, eeprom); 164 | assert(res == 1); 165 | fclose(eeprom); 166 | 167 | unpack_eeprom((uint8_t*)raw_file, size, output_path); 168 | 169 | free(raw_file); 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /eeprom-v2/sha256.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.c 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the SHA-256 hashing algorithm. 7 | SHA-256 is one of the three algorithms in the SHA2 8 | specification. The others, SHA-384 and SHA-512, are not 9 | offered in this implementation. 10 | Algorithm specification can be found here: 11 | * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf 12 | This implementation uses little endian byte order. 13 | *********************************************************************/ 14 | 15 | /*************************** HEADER FILES ***************************/ 16 | #include 17 | #include 18 | #include "sha256.h" 19 | 20 | /****************************** MACROS ******************************/ 21 | #define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) 22 | #define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) 23 | 24 | #define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) 25 | #define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 26 | #define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) 27 | #define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) 28 | #define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) 29 | #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) 30 | 31 | /**************************** VARIABLES *****************************/ 32 | static const WORD k[64] = { 33 | 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 34 | 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 35 | 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 36 | 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 37 | 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 38 | 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 39 | 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 40 | 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 41 | }; 42 | 43 | /*********************** FUNCTION DEFINITIONS ***********************/ 44 | void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) 45 | { 46 | WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; 47 | 48 | for (i = 0, j = 0; i < 16; ++i, j += 4) 49 | m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); 50 | for ( ; i < 64; ++i) 51 | m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; 52 | 53 | a = ctx->state[0]; 54 | b = ctx->state[1]; 55 | c = ctx->state[2]; 56 | d = ctx->state[3]; 57 | e = ctx->state[4]; 58 | f = ctx->state[5]; 59 | g = ctx->state[6]; 60 | h = ctx->state[7]; 61 | 62 | for (i = 0; i < 64; ++i) { 63 | t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; 64 | t2 = EP0(a) + MAJ(a,b,c); 65 | h = g; 66 | g = f; 67 | f = e; 68 | e = d + t1; 69 | d = c; 70 | c = b; 71 | b = a; 72 | a = t1 + t2; 73 | } 74 | 75 | ctx->state[0] += a; 76 | ctx->state[1] += b; 77 | ctx->state[2] += c; 78 | ctx->state[3] += d; 79 | ctx->state[4] += e; 80 | ctx->state[5] += f; 81 | ctx->state[6] += g; 82 | ctx->state[7] += h; 83 | } 84 | 85 | void sha256_init(SHA256_CTX *ctx) 86 | { 87 | ctx->datalen = 0; 88 | ctx->bitlen = 0; 89 | ctx->state[0] = 0x6a09e667; 90 | ctx->state[1] = 0xbb67ae85; 91 | ctx->state[2] = 0x3c6ef372; 92 | ctx->state[3] = 0xa54ff53a; 93 | ctx->state[4] = 0x510e527f; 94 | ctx->state[5] = 0x9b05688c; 95 | ctx->state[6] = 0x1f83d9ab; 96 | ctx->state[7] = 0x5be0cd19; 97 | } 98 | 99 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) 100 | { 101 | WORD i; 102 | 103 | for (i = 0; i < len; ++i) { 104 | ctx->data[ctx->datalen] = data[i]; 105 | ctx->datalen++; 106 | if (ctx->datalen == 64) { 107 | sha256_transform(ctx, ctx->data); 108 | ctx->bitlen += 512; 109 | ctx->datalen = 0; 110 | } 111 | } 112 | } 113 | 114 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]) 115 | { 116 | WORD i; 117 | 118 | i = ctx->datalen; 119 | 120 | // Pad whatever data is left in the buffer. 121 | if (ctx->datalen < 56) { 122 | ctx->data[i++] = 0x80; 123 | while (i < 56) 124 | ctx->data[i++] = 0x00; 125 | } 126 | else { 127 | ctx->data[i++] = 0x80; 128 | while (i < 64) 129 | ctx->data[i++] = 0x00; 130 | sha256_transform(ctx, ctx->data); 131 | memset(ctx->data, 0, 56); 132 | } 133 | 134 | // Append to the padding the total message's length in bits and transform. 135 | ctx->bitlen += ctx->datalen * 8; 136 | ctx->data[63] = ctx->bitlen; 137 | ctx->data[62] = ctx->bitlen >> 8; 138 | ctx->data[61] = ctx->bitlen >> 16; 139 | ctx->data[60] = ctx->bitlen >> 24; 140 | ctx->data[59] = ctx->bitlen >> 32; 141 | ctx->data[58] = ctx->bitlen >> 40; 142 | ctx->data[57] = ctx->bitlen >> 48; 143 | ctx->data[56] = ctx->bitlen >> 56; 144 | sha256_transform(ctx, ctx->data); 145 | 146 | // Since this implementation uses little endian byte ordering and SHA uses big endian, 147 | // reverse all the bytes when copying the final state to the output hash. 148 | for (i = 0; i < 4; ++i) { 149 | hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; 150 | hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; 151 | hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; 152 | hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; 153 | hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; 154 | hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; 155 | hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; 156 | hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /eeprom-v2/sha256.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA256_H 10 | #define SHA256_H 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | 15 | /****************************** MACROS ******************************/ 16 | #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest 17 | 18 | /**************************** DATA TYPES ****************************/ 19 | typedef unsigned char BYTE; // 8-bit byte 20 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines 21 | 22 | typedef struct { 23 | BYTE data[64]; 24 | WORD datalen; 25 | unsigned long long bitlen; 26 | WORD state[8]; 27 | } SHA256_CTX; 28 | 29 | /*********************** FUNCTION DECLARATIONS **********************/ 30 | void sha256_init(SHA256_CTX *ctx); 31 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); 32 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]); 33 | 34 | #endif // SHA256_H 35 | -------------------------------------------------------------------------------- /eeprom/SpiFs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | 4 | module SpiFs where 5 | 6 | import Control.Applicative 7 | import qualified Crypto.Hash.SHA256 as SHA256 8 | import Data.Binary 9 | import Data.Binary.Get 10 | import qualified Data.ByteString as BS 11 | import qualified Data.Text as T 12 | import qualified Data.Text.Encoding as T 13 | import Data.Word 14 | import GHC.Generics 15 | 16 | data FirmwareFile = FirmwareFile 17 | { firmwareEntries :: [ Entry ] 18 | } deriving (Generic, Show) 19 | 20 | instance Binary FirmwareFile where 21 | get = FirmwareFile <$> many readEntry 22 | put = undefined 23 | 24 | data Entry = Entry 25 | { entryOffset :: ByteOffset 26 | , entryMagic :: Word32 27 | , entrySize :: Word32 28 | , entryBody :: BS.ByteString 29 | , entryPadding :: ByteOffset 30 | } | FileEntry 31 | { entryFileOffset :: ByteOffset 32 | , entryFileMagic :: Word32 33 | , entryFileName :: T.Text 34 | , entryFileBody :: BS.ByteString 35 | , entryFileHashes :: Maybe (BS.ByteString, BS.ByteString) 36 | } | EntryPadding deriving Show 37 | 38 | data LayoutFile = LayoutFile 39 | { lfName :: Maybe T.Text 40 | , lfMagic :: Word32 41 | , lfPosition :: ByteOffset 42 | } deriving (Show, Generic) 43 | 44 | readEntry :: Get Entry 45 | readEntry = do 46 | offset <- bytesRead 47 | magic <- getWord32be 48 | len <- getWord32be 49 | body <- getByteString $ fromIntegral len 50 | tailOffset <- bytesRead 51 | let 52 | remainder = tailOffset `mod` 8 53 | paddingLen = if remainder == 0 then 0 else (8 - remainder) 54 | handlePlainFile :: Entry 55 | handlePlainFile = do 56 | let 57 | filename :: T.Text 58 | filename = T.decodeUtf8 $ BS.takeWhile (\c -> c /= 0) $ BS.take 16 body 59 | filebody = BS.drop 16 body 60 | FileEntry offset magic filename filebody Nothing 61 | handleHashedFile = do 62 | let 63 | (part1, middle) = BS.splitAt 16 body 64 | (filebody, hash) = BS.splitAt ((BS.length middle) - 32) middle 65 | filename :: T.Text 66 | filename = T.decodeUtf8 $ BS.takeWhile (\c -> c /= 0) part1 67 | actualHash = SHA256.hash (filebody) 68 | FileEntry offset magic filename (filebody <> hash) (Just (hash, actualHash)) 69 | handleFileMagic :: Word32 -> Entry 70 | handleFileMagic 0x55aaf00f = FileEntry offset magic "bootcode.bin" body Nothing 71 | handleFileMagic 0x55aaf11f = handlePlainFile 72 | handleFileMagic 0x55aaf22f = handlePlainFile 73 | handleFileMagic 0x55aaf33f = handleHashedFile 74 | handleFileMagic 0x55aafeef = EntryPadding 75 | handleFileMagic _ = Entry offset magic len body tailOffset 76 | _padding <- getByteString $ fromIntegral paddingLen 77 | pure $ handleFileMagic magic 78 | 79 | generateLayoutFile :: FirmwareFile -> [LayoutFile] 80 | generateLayoutFile = (map entryToLayoutFile) . (filter isPadding) . firmwareEntries 81 | where 82 | isPadding EntryPadding = True 83 | isPadding _ = False 84 | entryToLayoutFile :: Entry -> LayoutFile 85 | entryToLayoutFile (FileEntry position magic name _ _) = LayoutFile (Just name) magic position 86 | entryToLayoutFile (Entry position magic _ _ _) = LayoutFile Nothing magic position 87 | entryToLayoutFile (EntryPadding) = LayoutFile Nothing 0x55aaf33f 0 88 | -------------------------------------------------------------------------------- /eeprom/default.nix: -------------------------------------------------------------------------------- 1 | { haskellPackages, runCommand, lib }: 2 | 3 | let 4 | myghc = haskellPackages.ghcWithPackages (ps: with ps; [ formatting aeson cryptohash-sha256 base16-bytestring ]); 5 | in runCommand "extractor" { 6 | buildInputs = [ myghc ]; 7 | src = lib.cleanSource ./.; 8 | } '' 9 | unpackPhase 10 | cd $sourceRoot 11 | mkdir -pv $out/bin 12 | ghc ./extractor.hs -o $out/bin/extractor -Wall 13 | '' 14 | -------------------------------------------------------------------------------- /eeprom/extractor.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveGeneric #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE NamedFieldPuns #-} 4 | 5 | module Main where 6 | 7 | import Data.Binary 8 | --import Data.Binary.Get 9 | import qualified Data.ByteString as BS 10 | --import GHC.Generics 11 | --import Control.Applicative 12 | import System.Environment 13 | import Formatting ((%), sformat, hex, shown, stext) 14 | import qualified Data.Text as T 15 | --import qualified Data.Text.Encoding as T 16 | import qualified Data.ByteString.Base16 as Base16 17 | 18 | import SpiFs 19 | 20 | decodeFirmware :: FilePath -> IO FirmwareFile 21 | decodeFirmware = decodeFile 22 | 23 | main :: IO () 24 | main = do 25 | args <- getArgs 26 | let 27 | prettyPrintEntry :: Entry -> IO () 28 | prettyPrintEntry Entry{entryMagic, entryBody} = do 29 | let 30 | formatter = "Entry magic=" % hex % " initial_body=" % shown 31 | putStrLn $ T.unpack $ sformat formatter entryMagic (BS.take 32 entryBody) 32 | prettyPrintEntry FileEntry{entryFileMagic, entryFileName, entryFileBody, entryFileHashes} = do 33 | let 34 | formatter1 = "File Entry magic=" % hex % " name=" % stext % " body=" % shown 35 | formatter2 = "File Entry magic=" % hex % " name=" % stext % " body=hidden" 36 | formatter4 = "File Entry magic=" % hex % " name=" % stext % " hash1=" % shown % " hash2=" % shown 37 | go2 = putStrLn $ T.unpack $ sformat formatter1 entryFileMagic entryFileName (BS.take 100 entryFileBody) 38 | go3 = putStrLn $ T.unpack $ sformat formatter2 entryFileMagic entryFileName 39 | go4 hash1 hash2 = putStrLn $ T.unpack $ sformat formatter4 entryFileMagic entryFileName hash1 hash2 40 | case (entryFileName, entryFileHashes) of 41 | ("bootconf.txt", _) -> go2 42 | (_, Just (hash1, hash2)) -> go4 (Base16.encode hash1) (Base16.encode hash2) 43 | _ -> go3 44 | prettyPrintEntry EntryPadding = putStrLn $ "Padding" 45 | go :: [String] -> IO () 46 | go [file] = do 47 | parsed <- decodeFirmware file 48 | mapM_ prettyPrintEntry (firmwareEntries parsed) 49 | mapM_ extractFiles (firmwareEntries parsed) 50 | print $ generateLayoutFile parsed 51 | go args 52 | 53 | extractFiles :: Entry -> IO () 54 | extractFiles Entry{entryMagic, entryBody} 55 | | entryMagic == 0x55aaf00f = do 56 | BS.writeFile "bootcode.bin" entryBody 57 | | otherwise = pure () 58 | extractFiles FileEntry{entryFileName, entryFileBody, entryFileBodyCompressed} = do 59 | BS.writeFile (T.unpack entryFileName) entryFileBody 60 | let 61 | maybeWriteFile Nothing = pure () 62 | maybeWriteFile (Just body) = BS.writeFile ((T.unpack entryFileName) <> ".lzjb") body 63 | maybeWriteFile entryFileBodyCompressed 64 | extractFiles EntryPadding = pure () 65 | -------------------------------------------------------------------------------- /eeprom/extractor.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const bootcode = require("../signing-tool/bootcode.js"); 3 | 4 | function getStr(buf) { 5 | var len = buf.indexOf(0); 6 | return buf.toString("ascii", 0, len); 7 | } 8 | 9 | function process_eeprom(filename) { 10 | var res = filename.match(/beta|critical|stable/); 11 | var output = './'; 12 | if (res) { 13 | output += res[0] + "/"; 14 | } 15 | 16 | var res = filename.match(/(pieeprom-.*)\.bin/); 17 | if (res) { 18 | output += res[1] + "/"; 19 | } 20 | 21 | console.log(output); 22 | fs.mkdirSync(output, {recursive: true}); 23 | 24 | const full_eeprom = fs.readFileSync(filename); 25 | var pos = 0; 26 | while (pos < full_eeprom.length) { 27 | var magic = full_eeprom.readUint32BE(pos+0); 28 | var len = full_eeprom.readUint32BE(pos+4); 29 | var body = full_eeprom.slice(pos+8, pos+8+len); 30 | switch (magic) { 31 | case 0x55aaf00f: 32 | //console.log("bootcode.bin:", body); 33 | fs.writeFileSync(output+"bootcode.bin", body); 34 | var res = bootcode.parse_bootcode(body); 35 | fs.writeFileSync(output+"bootcode.info", JSON.stringify(res)); 36 | break; 37 | case 0x55aaf11f: 38 | var name = getStr(body.slice(0,16)); 39 | var real_body = body.slice(16); 40 | switch (name) { 41 | case "bootconf.txt": 42 | //console.log(name, real_body.toString("ascii")); 43 | break; 44 | default: 45 | //console.log('plain file', name, real_body); 46 | } 47 | fs.writeFileSync(output+name, real_body); 48 | break; 49 | case 0x55aaf33f: 50 | var name = getStr(body.slice(0,16)); 51 | var real_body = body.slice(16, body.length - 32); 52 | var hash = body.slice(body.length - 32); 53 | //console.log("compressed", name, real_body, hash.toString("hex")); 54 | fs.writeFileSync(output+name+".compressed", real_body); 55 | fs.writeFileSync(output+name+".hash", hash); 56 | break; 57 | case 0x55aafeef: 58 | console.log(`padding, ${len} bytes`); 59 | break; 60 | case 0xffffffff: 61 | console.log(`EOF, ${full_eeprom.length - pos} bytes left`); 62 | return; 63 | break; 64 | default: 65 | console.log(magic.toString(16), len, body); 66 | } 67 | pos += ((8+len-1) & 0xffff8) + 8; 68 | } 69 | } 70 | 71 | process_eeprom(process.argv[2]); 72 | -------------------------------------------------------------------------------- /elf-tool/Makefile: -------------------------------------------------------------------------------- 1 | elf-tool: elf-tool.c 2 | ${CC} $< -o $@ 3 | 4 | install: elf-tool 5 | mkdir -pv ${out}/bin 6 | cp elf-tool ${out}/bin/ 7 | -------------------------------------------------------------------------------- /elf-tool/default.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | 3 | rec { 4 | elf-tool = stdenv.mkDerivation { 5 | name = "elf-tool"; 6 | src = ./.; 7 | }; 8 | convert = { rom }: runCommandCC "convert" {nativeBuildInputs = [ elf-tool file util-linux ]; } '' 9 | mkdir $out/ 10 | elf-tool -i ${rom} -o $out/out.elf 11 | ls -lh $out/ 12 | file $out/out.elf 13 | readelf -hl $out/out.elf 14 | ''; 15 | } 16 | -------------------------------------------------------------------------------- /elf-tool/elf-tool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) 13 | 14 | int main(int argc, char **argv) { 15 | int opt; 16 | char *in = NULL; 17 | char *out = NULL; 18 | bool rom = true; 19 | while ((opt = getopt(argc, argv, "i:o:")) != -1) { 20 | switch (opt) { 21 | case 'i': 22 | in = optarg; 23 | break; 24 | case 'o': 25 | out = optarg; 26 | break; 27 | } 28 | } 29 | assert(in); 30 | assert(out); 31 | int rom_in = open(in, O_RDONLY); 32 | int elf_out = open(out, O_RDWR | O_CREAT, 0644); 33 | 34 | assert(rom_in > 0); 35 | assert(elf_out > 0); 36 | 37 | uint64_t bin_size = lseek(rom_in, 0, SEEK_END); 38 | uint8_t *wholebin = malloc(bin_size); 39 | //printf("%p\n", wholebin); 40 | pread(rom_in, wholebin, bin_size, 0); 41 | 42 | uint32_t dests[4]; 43 | uint32_t srcs[4]; 44 | int32_t sizes[4]; 45 | int next_slot = 0; 46 | 47 | uint32_t magic = 0x02494e49; 48 | uint32_t *casted = (uint32_t*)wholebin; 49 | for (int i=0; i<(bin_size/4); i++) { 50 | if (casted[i] == magic) { 51 | printf("found at 0x%x\n", i*4); 52 | uint32_t *load = &casted[i+1]; 53 | while (load[0] && load[1]) { 54 | assert(next_slot < 4); 55 | uint32_t addr = load[0]; 56 | int32_t size = load[1]; 57 | printf("0x%x %d\n", addr, size); 58 | dests[next_slot] = addr; 59 | if (size > 0) { 60 | srcs[next_slot] = ((uint8_t*)&load[2]) - wholebin; 61 | } 62 | sizes[next_slot] = size; 63 | if (size > 0) { 64 | load = load + ((8 + (ROUNDUP(size,4)))/4); 65 | } else { 66 | load = load + 2; 67 | } 68 | //printf("%p\n", load); 69 | next_slot++; 70 | } 71 | } 72 | } 73 | 74 | Elf32_Ehdr elf_header; 75 | Elf32_Phdr phdr[1 + next_slot]; 76 | 77 | bzero(&elf_header, sizeof(elf_header)); 78 | bzero(&phdr, sizeof(phdr)); 79 | 80 | elf_header.e_ident[EI_MAG0] = ELFMAG0; 81 | elf_header.e_ident[EI_MAG1] = ELFMAG1; 82 | elf_header.e_ident[EI_MAG2] = ELFMAG2; 83 | elf_header.e_ident[EI_MAG3] = ELFMAG3; 84 | elf_header.e_ident[EI_CLASS] = ELFCLASS32; 85 | elf_header.e_ident[EI_DATA] = ELFDATA2LSB; 86 | elf_header.e_ident[EI_VERSION] = EV_CURRENT; 87 | elf_header.e_ident[EI_OSABI] = ELFOSABI_SYSV; 88 | //elf_header.e_ident[EI_ABIVERSION] = ; 89 | elf_header.e_ident[EI_NIDENT] = sizeof(elf_header.e_ident); 90 | elf_header.e_type = ET_EXEC; 91 | elf_header.e_machine = 137; // VC4 92 | elf_header.e_version = EV_CURRENT; 93 | if (rom) elf_header.e_entry = 0x60000000; 94 | elf_header.e_phoff = 64; // offset to program header 95 | elf_header.e_phentsize = sizeof(phdr[0]); 96 | elf_header.e_phnum = sizeof(phdr) / sizeof(phdr[0]); 97 | 98 | phdr[0].p_type = PT_LOAD; 99 | phdr[0].p_offset = 0x1000; 100 | phdr[0].p_vaddr = 0x60000000; 101 | phdr[0].p_paddr = phdr[0].p_vaddr; 102 | phdr[0].p_filesz = bin_size; 103 | phdr[0].p_memsz = bin_size; 104 | phdr[0].p_flags = PF_X | PF_R; 105 | phdr[0].p_align = 0; 106 | 107 | for (int i=0; i 0) { 110 | phdr[1+i].p_offset = 0x1000 + srcs[i]; 111 | phdr[1+i].p_filesz = sizes[i]; 112 | } 113 | phdr[1+i].p_vaddr = phdr[1+i].p_paddr = dests[i]; 114 | phdr[1+i].p_memsz = abs(sizes[i]); 115 | } 116 | 117 | pwrite(elf_out, &elf_header, sizeof(elf_header), 0); 118 | pwrite(elf_out, &phdr, sizeof(phdr), 64); 119 | 120 | pwrite(elf_out, wholebin, bin_size, 0x1000); 121 | 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /extractor.nix: -------------------------------------------------------------------------------- 1 | { runCommand, eeprom-extractor, rpi-eeprom, time }: 2 | 3 | runCommand "extracted" { buildInputs = [ eeprom-extractor time ]; } '' 4 | for channel in beta critical stable; do 5 | for file in ${rpi-eeprom}/firmware/$channel/pieeprom*.bin; do 6 | cp -v $file $channel-$(basename $file) 7 | done 8 | done 9 | mkdir $out 10 | cd $out 11 | for file in $NIX_BUILD_TOP/*.bin; do 12 | name=$(basename $file) 13 | name=''${name%.*} 14 | echo $file X ''$name 15 | mkdir $name 16 | pushd $name 17 | command time -v extractor $file 18 | popd 19 | done 20 | '' 21 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "firmware": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1613486701, 7 | "narHash": "sha256-L3qchuLwhujKcoFiVnL3Ao6b4iKRI/w113EENOpWMNE=", 8 | "owner": "raspberrypi", 9 | "repo": "firmware", 10 | "rev": "ba6259246c702b04ea56ff1034325e476d460ae8", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "raspberrypi", 15 | "repo": "firmware", 16 | "type": "github" 17 | } 18 | }, 19 | "nixpkgs": { 20 | "locked": { 21 | "lastModified": 1698336494, 22 | "narHash": "sha256-sO72WDBKyijYD1GcKPlGsycKbMBiTJMBCnmOxLAs880=", 23 | "owner": "NixOS", 24 | "repo": "nixpkgs", 25 | "rev": "808c0d8c53c7ae50f82aca8e7df263225cf235bf", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "id": "nixpkgs", 30 | "type": "indirect" 31 | } 32 | }, 33 | "nixpkgs-be": { 34 | "locked": { 35 | "lastModified": 1637959927, 36 | "narHash": "sha256-NhfBtrRwGZK9NuPR3DGa7fk6l+XK07vvEYJyyz8Edok=", 37 | "owner": "cleverca22", 38 | "repo": "nixpkgs", 39 | "rev": "c865a2a7dd68d0d7f3f7b5bdda951feba0bc05c4", 40 | "type": "github" 41 | }, 42 | "original": { 43 | "owner": "cleverca22", 44 | "ref": "aarch64-be", 45 | "repo": "nixpkgs", 46 | "type": "github" 47 | } 48 | }, 49 | "root": { 50 | "inputs": { 51 | "firmware": "firmware", 52 | "nixpkgs": "nixpkgs", 53 | "nixpkgs-be": "nixpkgs-be", 54 | "rpi-eeprom": "rpi-eeprom", 55 | "rpi-open-firmware": "rpi-open-firmware", 56 | "utils": "utils" 57 | } 58 | }, 59 | "rpi-eeprom": { 60 | "flake": false, 61 | "locked": { 62 | "lastModified": 1607946172, 63 | "narHash": "sha256-E1/doS2LVpOgJutXNYJweecMQM10RUiujt5YQohd53o=", 64 | "owner": "raspberrypi", 65 | "repo": "rpi-eeprom", 66 | "rev": "54a9796abbee59067bff9da6b90c1014178f2c21", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "raspberrypi", 71 | "repo": "rpi-eeprom", 72 | "type": "github" 73 | } 74 | }, 75 | "rpi-open-firmware": { 76 | "flake": false, 77 | "locked": { 78 | "lastModified": 1627196750, 79 | "narHash": "sha256-3MdVYk1z1Ro7QXGsWMoV0yTc5SLptO+oonTWQPi90sA=", 80 | "owner": "librerpi", 81 | "repo": "rpi-open-firmware", 82 | "rev": "418f43903320b31e7846223c95448fe3f44b506c", 83 | "type": "github" 84 | }, 85 | "original": { 86 | "owner": "librerpi", 87 | "repo": "rpi-open-firmware", 88 | "type": "github" 89 | } 90 | }, 91 | "utils": { 92 | "locked": { 93 | "lastModified": 1609246779, 94 | "narHash": "sha256-eq6ZXE/VWo3EMC65jmIT6H/rrUc9UWOWVujkzav025k=", 95 | "owner": "numtide", 96 | "repo": "flake-utils", 97 | "rev": "08c7ad4a0844adc4a7f9f5bb3beae482e789afa4", 98 | "type": "github" 99 | }, 100 | "original": { 101 | "owner": "numtide", 102 | "repo": "flake-utils", 103 | "type": "github" 104 | } 105 | } 106 | }, 107 | "root": "root", 108 | "version": 7 109 | } 110 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | utils.url = "github:numtide/flake-utils"; 4 | rpi-eeprom = { 5 | url = "github:raspberrypi/rpi-eeprom"; 6 | flake = false; 7 | }; 8 | rpi-open-firmware = { 9 | url = "github:librerpi/rpi-open-firmware"; 10 | flake = false; 11 | }; 12 | firmware = { 13 | url = "github:raspberrypi/firmware"; 14 | flake = false; 15 | }; 16 | nixpkgs-be.url = "github:cleverca22/nixpkgs/aarch64-be"; 17 | #nixpkgs.url = "path:/home/clever/apps/rpi/nixpkgs-test"; 18 | }; 19 | outputs = { self, nixpkgs, utils, rpi-eeprom, rpi-open-firmware, firmware, nixpkgs-be }: 20 | utils.lib.eachSystem [ "x86_64-linux" "armv7l-linux" "armv6l-linux" "aarch64-linux" "aarch64_be-linux" ] (system: 21 | let 22 | old_system = rec { 23 | common = import ./nix/common.nix; 24 | msd = import ./nix/msd.nix; 25 | keyboard = import ./nix/keyboard.nix; 26 | android-auto = import ./nix/android-auto.nix; 27 | pi0-swd = import ./nix/pi0-swd.nix; 28 | pkgs = import nixpkgs { 29 | overlays = [ common msd android-auto keyboard pi0-swd ]; 30 | system = "aarch64-linux"; 31 | }; 32 | }; 33 | overlay = self: super: { 34 | inherit rpi-eeprom firmware; 35 | eeprom-extractor = self.callPackage ./eeprom {}; 36 | extracted = self.callPackage ./extractor.nix {}; 37 | utils = super.callPackage ./utils {}; 38 | common = self.callPackage "${rpi-open-firmware}/common" {}; 39 | tlsf = null; 40 | initrd_basic = self.callPackage ./initrd.nix {}; 41 | e2fsprogs = null; 42 | zstd = null; 43 | edid = self.callPackage ./edid {}; 44 | valgrind = super.valgrind.overrideAttrs (old: { 45 | patches = [ ./valgrind.patch ]; 46 | }); 47 | valgrind-light = super.valgrind-light.overrideAttrs (old: { 48 | patches = [ ./valgrind.patch ]; 49 | }); 50 | }; 51 | systemTable = { 52 | x86_64-linux = "x86_64-linux"; 53 | armv7l-linux = "armv7l-linux"; 54 | armv6l-linux = "armv6l-linux"; 55 | aarch64-linux = "aarch64-linux"; 56 | }; 57 | crosser = { 58 | x86_64-linux = x: x; 59 | armv6l-linux = x: x; 60 | armv7l-linux = x: x; 61 | aarch64-linux = x: x; 62 | #armv7l-linux = x: x.pkgsCross.armv7l-hf-multiplatform; 63 | #armv6l-linux = x: x.pkgsCross.raspberryPi; 64 | }; 65 | pkgs = crosser.${system} (import nixpkgs { system = systemTable.${system}; overlays = [ overlay ]; }); 66 | in { 67 | packages = (nixpkgs.lib.optionalAttrs (system != "aarch64_be-linux") { 68 | inherit (pkgs) extracted eeprom-extractor utils nix initrd_basic edid; 69 | }) // (nixpkgs.lib.optionalAttrs (system == "aarch64_be-linux") (let 70 | be = import ./big-endian { inherit nixpkgs-be; }; 71 | in { 72 | inherit (be) diskImage script; 73 | cross-bootstrap = (import (nixpkgs-be + "/pkgs/stdenv/linux/make-bootstrap-tools-cross.nix") { system = "x86_64-linux"; }).aarch64_be.dist; 74 | })); 75 | hydraJobs = (nixpkgs.lib.optionalAttrs (system == "aarch64_be-linux") (let 76 | in { 77 | inherit (self.outputs.packages.${system}) diskImage script cross-bootstrap; 78 | })) // (nixpkgs.lib.optionalAttrs (system == "x86_64-linux") { 79 | pi400_keyboard = old_system.pkgs.keyboard.rootZip; 80 | }); 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /initrd.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib }: 2 | 3 | let 4 | eval = lib.evalModules { 5 | prefix = []; 6 | check = true; 7 | modules = [ ./initrd/core.nix ./initrd/pi400-keyboard.nix ]; 8 | args = { 9 | initialPkgs = pkgs; 10 | }; 11 | }; 12 | in eval.config.results.rootDir // { inherit eval; } 13 | -------------------------------------------------------------------------------- /initrd/core.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, initialPkgs, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | options = { 7 | results = mkOption { 8 | internal = true; 9 | default = {}; 10 | type = types.attrs; 11 | }; 12 | utils = mkOption { 13 | default = {}; 14 | }; 15 | overlays = mkOption { 16 | default = []; 17 | }; 18 | install.avahi = mkOption { 19 | type = types.bool; 20 | default = false; 21 | }; 22 | install.openocd = mkOption { 23 | type = types.bool; 24 | default = false; 25 | }; 26 | install.dbus = mkOption { 27 | type = types.bool; 28 | default = false; 29 | }; 30 | install.systemd = mkOption { 31 | type = types.bool; 32 | default = false; 33 | }; 34 | install.dropbear = mkOption { 35 | type = types.bool; 36 | default = false; 37 | }; 38 | wifi = mkOption { 39 | type = types.bool; 40 | default = false; 41 | }; 42 | trimRootDir = mkOption { 43 | default = ""; 44 | type = types.lines; 45 | }; 46 | initScript = mkOption { 47 | }; 48 | initrd_script = mkOption { 49 | default = ""; 50 | type = types.lines; 51 | }; 52 | modulesForKernels = mkOption { 53 | }; 54 | kernel_versions = mkOption { 55 | default = map (x: "${config.kernel_version}${x}") config.kernelVersionList; 56 | }; 57 | kernel_version = mkOption { 58 | type = types.str; 59 | default = "5.10.16"; 60 | }; 61 | kernelVersionList = mkOption { 62 | default = [ 63 | "+" # pi0 64 | "-v7+" 65 | "-v7l+" 66 | "-v8+" 67 | ]; 68 | }; 69 | extra_modules = mkOption { 70 | default = []; 71 | type = types.listOf types.str; 72 | }; 73 | installedPackages = mkOption { 74 | }; 75 | extra_utils = mkOption { 76 | default = []; 77 | }; 78 | shrunkenPackages = mkOption { 79 | }; 80 | etc = mkOption { 81 | }; 82 | }; 83 | config = { 84 | _module.args = { 85 | pkgs = builtins.foldl' (super: super.extend) initialPkgs config.overlays; 86 | }; 87 | modulesForKernels = pkgs.buildEnv { 88 | name = "all-the-modules"; 89 | paths = (map config.utils.moduleClosureForKernel config.kernel_versions) ++ lib.optionals config.wifi [ pkgs.wireless-regdb pkgs.raspberrypiWirelessFirmware ]; 90 | pathsToLink = [ "/lib" ]; 91 | ignoreCollisions = true; 92 | }; 93 | # see also: 94 | # https://elinux.org/images/e/ef/USB_Gadget_Configfs_API_0.pdf 95 | initScript = pkgs.writeTextFile { 96 | name = "init"; 97 | text = '' 98 | #!/bin/ash 99 | 100 | mount -t proc proc proc 101 | mount -t sysfs sys sys 102 | mount -t devtmpfs dev dev 103 | mount -t configfs none /sys/kernel/config 104 | mkdir /dev/pts 105 | mount -t devpts devpts /dev/pts 106 | mount -t debugfs debugfs /sys/kernel/debug 107 | 108 | boottime 109 | 110 | depmod 111 | serial=$(cut -c9-16 < /proc/device-tree/serial-number) 112 | hostname pi-''${serial} 113 | 114 | ${config.initrd_script} 115 | 116 | boottime 117 | 118 | exec ash 119 | ''; 120 | executable = true; 121 | }; 122 | overlays = [ (self: super: { 123 | smi-test = self.stdenv.mkDerivation { 124 | name = "smi-test"; 125 | unpackPhase = '' 126 | cp ${../smi-test.c} smi-test.c 127 | export sourceRoot=. 128 | ''; 129 | buildPhase = '' 130 | $CC smi-test.c -o smi-test -I ${self.linux_rpi0.src}/include/ 131 | ''; 132 | installPhase = '' 133 | mkdir -p $out/bin 134 | cp smi-test $out/bin 135 | ''; 136 | }; 137 | utillinux = super.utillinux.override { systemd = null; }; 138 | shrunken_busybox = self.runCommand "shrunk-busybox" { 139 | busybox = self.busybox.override { enableStatic=true; }; 140 | nativeBuildInputs = [ self.buildPackages.nukeReferences ]; 141 | } '' 142 | mkdir $out 143 | cp -vir $busybox/bin $out/ 144 | chmod +w $out/bin 145 | chmod +w $out/bin/busybox 146 | nuke-refs $out/bin/busybox 147 | ''; 148 | baseFirmware = self.runCommand "base-firmware" { 149 | inherit (self) firmware; 150 | } '' 151 | mkdir $out 152 | cp -r $firmware/boot/{*.dtb,kernel*img,fixup*dat,start*elf,overlays} $out/ 153 | ''; 154 | }) ]; 155 | etc = pkgs.callPackage ({ runCommand, nukeReferences }: runCommand "etc" { 156 | nsswitch = '' 157 | passwd: files systemd 158 | group: files systemd 159 | shadow: files 160 | 161 | hosts: files mymachines mdns_minimal [NOTFOUND=return] dns mdns myhostname 162 | networks: files 163 | 164 | ethers: files 165 | services: files 166 | protocols: files 167 | rpc: files 168 | ''; 169 | # sets root password to password 170 | passwd = '' 171 | root:nxz2xIegZ0Ytc:0:0:System administrator:/:/bin/sh 172 | avahi:x:10:10:avahi-daemon privilege separation user:/var/empty:/run/current-system/sw/bin/nologin 173 | sshd:x:498:65534:SSH privilege separation user:/var/empty:/run/current-system/sw/bin/nologin 174 | nscd:x:2:2:nscd privilege separation user:/var/empty:/run/current-system/sw/bin/nologin 175 | ''; 176 | group = '' 177 | avahi:x:10: 178 | ''; 179 | sshd_config = '' 180 | UsePAM no 181 | Port 22 182 | ''; 183 | nscd = '' 184 | server-user nscd 185 | 186 | enable-cache passwd yes 187 | positive-time-to-live passwd 0 188 | negative-time-to-live passwd 0 189 | shared passwd yes 190 | 191 | enable-cache group yes 192 | positive-time-to-live group 0 193 | negative-time-to-live group 0 194 | shared group yes 195 | 196 | enable-cache netgroup yes 197 | positive-time-to-live netgroup 0 198 | negative-time-to-live netgroup 0 199 | shared netgroup yes 200 | 201 | enable-cache hosts yes 202 | positive-time-to-live hosts 0 203 | negative-time-to-live hosts 0 204 | shared hosts yes 205 | 206 | enable-cache services yes 207 | positive-time-to-live services 0 208 | negative-time-to-live services 0 209 | shared services yes 210 | ''; 211 | passAsFile = [ "nsswitch" "passwd" "sshd_config" "nscd" "group" ]; 212 | nativeBuildInputs = [ nukeReferences ]; 213 | } '' 214 | mkdir -p $out/ssh 215 | cd $out 216 | ${lib.optionalString config.wifi '' 217 | cp ${../wpa_supplicant.conf} wpa_supplicant.conf 218 | ''} 219 | ${lib.optionalString config.install.avahi '' 220 | cp -r ${pkgs.avahi}/etc/avahi avahi 221 | chmod +w -R avahi 222 | for x in avahi/avahi-autoipd.action avahi/avahi-dnsconfd.action; do 223 | nuke-refs $x 224 | done 225 | ''} 226 | cp $nsswitchPath nsswitch.conf 227 | cp $passwdPath passwd 228 | cp $groupPath group 229 | cp $sshd_configPath ssh/sshd_config 230 | cp $nscdPath nscd.conf 231 | '') {}; 232 | shrunkenPackages = pkgs.callPackage ({runCommandCC, nukeReferences }: runCommandCC "shrunken-packages" { nativeBuildInputs = [ nukeReferences ]; } '' 233 | mkdir -p $out/{bin,lib} 234 | ${lib.optionalString config.install.openocd '' 235 | cp ${pkgs.openocd}/bin/openocd $out/bin 236 | ''} 237 | ${lib.optionalString config.wifi '' 238 | cp ${pkgs.wpa_supplicant}/bin/wpa_supplicant $out/bin 239 | ''} 240 | ${lib.optionalString config.install.avahi '' 241 | cp ${pkgs.avahi}/bin/avahi-daemon $out/bin 242 | ''} 243 | cp ${pkgs.strace}/bin/strace $out/bin 244 | cp ${pkgs.openssh}/bin/sshd $out/bin 245 | #cp {pkgs.iproute}/bin/ip $out/bin 246 | ${lib.optionalString config.install.dropbear '' 247 | cp ${pkgs.dropbear}/bin/dropbear $out/bin/ 248 | ''} 249 | cp ${pkgs.glibcCross.bin}/bin/nscd $out/bin 250 | cp ${pkgs.smi-test}/bin/smi-test $out/bin 251 | 252 | cp ${pkgs.hidapi}/lib/libhidapi-hidraw.so.0 $out/lib 253 | cp ${pkgs.libusb1}/lib/libusb-1.0.so.0 $out/lib 254 | cp ${pkgs.glibcCross}/lib/lib{m.so.6,dl.so.2,pthread.so.0,c.so.6,rt.so.1,util.so.1,crypt.so.1,resolv.so.2,nss_files.so.2} $out/lib 255 | cp $(cat $(cat $NIX_CC/nix-support/orig-cc)/nix-support/propagated-build-inputs)/${pkgs.stdenv.targetPlatform.config}/lib/lib{gcc_s.so.1,ssp.so.0} $out/lib/ 256 | cp ${pkgs.utillinux.out}/lib/lib{mount.so.1,blkid.so.1,uuid.so.1} $out/lib/ 257 | cp ${pkgs.openssl.out}/lib/lib{ssl.so.1.1,crypto.so.1.1} $out/lib 258 | cp ${pkgs.libnl.out}/lib/li{bnl-3.so.200,bnl-genl-3.so.200} $out/lib 259 | ${lib.optionalString config.wifi '' 260 | cp ${pkgs.pcsclite.out}/lib/libpcsclite.so.1 $out/lib 261 | ''} 262 | ${lib.optionalString config.install.dbus '' 263 | cp ${pkgs.dbus.lib}/lib/libdbus-1.so.3 $out/lib 264 | ''} 265 | ${lib.optionalString config.install.systemd '' 266 | cp ${pkgs.systemd}/lib/libsystemd.so.0 $out/lib 267 | cp ${pkgs.udev}/lib/libudev.so.1 $out/lib 268 | ''} 269 | cp ${pkgs.xz.out}/lib/liblzma.so.5 $out/lib 270 | cp ${pkgs.lz4.out}/lib/liblz4.so.1 $out/lib 271 | cp ${pkgs.libcap.lib}/lib/libcap.so.2 $out/lib 272 | cp ${pkgs.libgcrypt.out}/lib/libgcrypt.so.20 $out/lib 273 | cp ${pkgs.libgpgerror}/lib/libgpg-error.so.0 $out/lib 274 | ${lib.optionalString config.install.avahi '' 275 | cp ${pkgs.avahi}/lib/lib{avahi-common.so.3,avahi-core.so.7} $out/lib 276 | ''} 277 | cp ${pkgs.libdaemon}/lib/libdaemon.so.0 $out/lib 278 | cp ${pkgs.expat}/lib/libexpat.so.1 $out/lib 279 | cp ${pkgs.libunwind}/lib/lib{unwind-ptrace.so.0,unwind-arm.so.8,unwind.so.8} $out/lib 280 | cp ${pkgs.pam}/lib/libpam.so.0 $out/lib 281 | cp ${pkgs.zlib}/lib/libz.so.1 $out/lib 282 | cp ${pkgs.libkrb5}/lib/lib{gssapi_krb5.so.2,krb5.so.3,k5crypto.so.3,com_err.so.3,krb5support.so.0} $out/lib 283 | cp ${pkgs.keyutils.lib}/lib/libkeyutils.so.1 $out/lib 284 | 285 | linker=$(basename $(cat $NIX_CC/nix-support/dynamic-linker)) 286 | chmod +w -R $out 287 | 288 | ${lib.optionalString config.install.openocd '' 289 | sed -i -e 's@${pkgs.openocd}@/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${pkgs.openocd.name}@' $out/bin/openocd 290 | sed -i -e 's@${pkgs.openocd}@/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${pkgs.openocd.name}@' $out/bin/openocd 291 | ''} 292 | for x in $out/lib/lib{pthread.so.0,gcc_s.so.1,rt.so.1,mount.so.1,crypto.so.1.0.0,dbus-1.so.3,gpg-error.so.0,ssp.so.0,daemon.so.0,avahi-common.so.3,pam.so.0,util.so.1,z.so.1,crypt.so.1,resolv.so.2,gssapi_krb5.so.2,krb5.so.3,k5crypto.so.3,com_err.so.3,nss_files.so.2}; do 293 | nuke-refs $x 294 | done 295 | nuke-refs $out/bin/avahi-daemon 296 | nuke-refs $out/bin/sshd 297 | nuke-refs $out/bin/nscd 298 | 299 | for bin in $out/bin/*; do 300 | patchelf --set-rpath $out/lib --set-interpreter $out/lib/$linker $bin 301 | done 302 | for lib in $out/lib/*; do 303 | patchelf --set-rpath $out/lib $lib 304 | done 305 | 306 | cp $(cat $NIX_CC/nix-support/dynamic-linker) $out/lib/$linker 307 | chmod +w -R $out 308 | $STRIP $out/lib/$linker 309 | nuke-refs $out/lib/libc.so* 310 | nuke-refs $out/lib/libdl.so.2 311 | nuke-refs $out/lib/libm.so.6 312 | sed -i -e 's@${pkgs.glibcCross.out}@/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${pkgs.glibcCross.name}@' $out/lib/$linker 313 | '') {}; 314 | installedPackages = pkgs.buildEnv { 315 | name = "bin"; 316 | paths = [ 317 | pkgs.shrunken_busybox 318 | #self.boottime 319 | #self.gdb 320 | config.shrunkenPackages 321 | ] ++ config.extra_utils; 322 | }; 323 | utils = { 324 | modulesForKernel = kernel: pkgs.runCommand "modules-${kernel}" {} '' 325 | mkdir -pv $out/lib/modules/ 326 | cp -r ${pkgs.firmware}/modules/${kernel} $out/lib/modules/ 327 | ''; 328 | moduleClosureForKernel = kernel: pkgs.makeModulesClosure { 329 | kernel = config.utils.modulesForKernel kernel; 330 | firmware = pkgs.buildEnv { 331 | name = "all-the-firmware"; 332 | paths = [ pkgs.firmwareLinuxNonfree pkgs.raspberrypiWirelessFirmware ]; 333 | ignoreCollisions = true; 334 | }; 335 | allowMissing = true; 336 | rootModules = [ 337 | "dwc2" 338 | "usb_f_acm" 339 | "usb_f_rndis" 340 | "usb_f_mass_storage" 341 | "gadgetfs" # for custom userland gadgets 342 | "usb_f_hid" 343 | "usb_f_rndis" 344 | ] ++ config.extra_modules; 345 | }; 346 | }; 347 | results.closure = pkgs.runCommand "closure-helper" {} '' 348 | mkdir $out 349 | cd $out 350 | ln -s ${config.initScript} init 351 | ln -s ${config.modulesForKernels} modules 352 | ln -s ${config.installedPackages} bin 353 | ln -s ${config.etc} etc 354 | ''; 355 | results.initrd = pkgs.makeInitrd { 356 | contents = [ 357 | { 358 | symlink = "/init"; 359 | object = config.initScript; 360 | } 361 | { 362 | symlink = "/lib/modules"; 363 | object = "${config.modulesForKernels}/lib/modules"; 364 | } 365 | { 366 | symlink = "/lib/firmware"; 367 | object = "${config.modulesForKernels}/lib/firmware"; 368 | } 369 | { 370 | symlink = "/bin"; 371 | object = "${config.installedPackages}/bin"; 372 | } 373 | { 374 | symlink = "/etc"; 375 | object = config.etc; 376 | } 377 | ]; 378 | }; 379 | results.rootDir = pkgs.runCommand "rootdir" {} '' 380 | mkdir $out 381 | cd $out 382 | ln -s ${pkgs.baseFirmware}/* . 383 | ln -s ${config.results.initrd}/initrd initrd 384 | ls -lLhs initrd 385 | cat < config.txt 386 | dtoverlay=dwc2 387 | dtoverlay=smi 388 | dtoverlay=smi-dev 389 | enable_uart=1 390 | uart_2ndstage=1 391 | dtoverlay=disable-bt 392 | 393 | initramfs initrd followkernel 394 | EOF 395 | 396 | cat < cmdline.txt 397 | nada console=tty1 console=serial0,115200 398 | EOF 399 | 400 | ${config.trimRootDir} 401 | ''; 402 | }; 403 | } 404 | -------------------------------------------------------------------------------- /initrd/pi400-keyboard.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | with lib; 4 | { 5 | options = { 6 | pi400-keyboard = lib.mkOption { 7 | type = types.bool; 8 | default = true; 9 | }; 10 | }; 11 | config = lib.mkIf config.pi400-keyboard { 12 | overlays = [ 13 | (self: super: { 14 | sendkey = self.writeScriptBin "sendkey" '' 15 | #!/bin/ash 16 | echo -ne \\x00\\x00\\x''${1}\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00 > /dev/hidg0 17 | ''; 18 | keyboard_proxy = self.stdenv.mkDerivation { 19 | name = "keyboard-proxy"; 20 | src = ../keyboard-proxy; 21 | buildPhase = '' 22 | $CC main.c -o keyboard-proxy -Wall 23 | ''; 24 | installPhase = '' 25 | mkdir -pv $out/bin/ 26 | cp keyboard-proxy $out/bin/ 27 | ''; 28 | }; 29 | }) 30 | ]; 31 | extra_utils = [ pkgs.sendkey pkgs.keyboard_proxy ]; 32 | initrd_script = '' 33 | modprobe -v dwc2 34 | modprobe usb_f_hid 35 | 36 | cd /sys/kernel/config/usb_gadget 37 | mkdir g1 38 | cd g1 39 | 40 | mkdir functions/hid.foo 41 | cd functions/hid.foo 42 | echo 1 > protocol 43 | echo 1 > subclass 44 | echo 8 > report_length 45 | echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > report_desc 46 | cd ../.. 47 | 48 | mkdir configs/c.1 49 | mkdir configs/c.1/strings/0x409 50 | echo "keyboard mode" > configs/c.1/strings/0x409/configuration 51 | 52 | mkdir strings/0x409 53 | echo "cleverca22" > strings/0x409/manufacturer 54 | echo "rpi-tools pi400 keyboard" > strings/0x409/product 55 | grep Serial /proc/cpuinfo | cut -c19-26 > strings/0x409/serialnumber 56 | 57 | ln -sv functions/hid.foo configs/c.1 58 | 59 | echo fe980000.usb > UDC 60 | 61 | cd / 62 | sleep 5 63 | keyboard-proxy & 64 | ''; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /keyboard-proxy/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // see also: https://www.devever.net/~hl/usbnkro 14 | 15 | #define MAX_EVENTS 10 16 | 17 | static const uint8_t key_mappings[KEY_MAX + 1] = { 18 | [0 ... KEY_MAX] = 0x0, 19 | [KEY_A] = 0x04, 20 | [KEY_B] = 0x05, 21 | [KEY_C] = 0x06, 22 | [KEY_D] = 0x07, 23 | [KEY_E] = 0x08, 24 | [KEY_F] = 0x09, 25 | [KEY_G] = 0x0a, 26 | [KEY_H] = 0x0b, 27 | [KEY_I] = 0x0c, 28 | [KEY_J] = 0x0d, 29 | [KEY_K] = 0x0e, 30 | [KEY_L] = 0x0f, 31 | [KEY_M] = 0x10, 32 | [KEY_N] = 0x11, 33 | [KEY_O] = 0x12, 34 | [KEY_P] = 0x13, 35 | [KEY_Q] = 0x14, 36 | [KEY_R] = 0x15, 37 | [KEY_S] = 0x16, 38 | [KEY_T] = 0x17, 39 | [KEY_U] = 0x18, 40 | [KEY_V] = 0x19, 41 | [KEY_W] = 0x1a, 42 | [KEY_X] = 0x1b, 43 | [KEY_Y] = 0x1c, 44 | [KEY_Z] = 0x1d, 45 | 46 | [KEY_1] = 0x1e, 47 | [KEY_2] = 0x1f, 48 | [KEY_3] = 0x20, 49 | [KEY_4] = 0x21, 50 | [KEY_5] = 0x22, 51 | [KEY_6] = 0x23, 52 | [KEY_7] = 0x24, 53 | [KEY_8] = 0x25, 54 | [KEY_9] = 0x26, 55 | [KEY_0] = 0x27, 56 | 57 | [KEY_ENTER] = 0x28, 58 | [KEY_ESC] = 0x29, 59 | [KEY_BACKSPACE] = 0x2a, 60 | [KEY_TAB] = 0x2b, 61 | [KEY_SPACE] = 0x2c, 62 | [KEY_MINUS] = 0x2d, 63 | [KEY_EQUAL] = 0x2e, 64 | [KEY_LEFTBRACE] = 0x2f, 65 | [KEY_RIGHTBRACE]= 0x30, 66 | [KEY_BACKSLASH] = 0x31, 67 | [KEY_SEMICOLON] = 0x33, 68 | [KEY_APOSTROPHE]= 0x34, 69 | [KEY_GRAVE] = 0x35, 70 | [KEY_COMMA] = 0x36, 71 | [KEY_DOT] = 0x37, 72 | [KEY_SLASH] = 0x38, 73 | [KEY_CAPSLOCK] = 0x39, 74 | [KEY_F1] = 0x3a, 75 | [KEY_F2] = 0x3b, 76 | [KEY_F3] = 0x3c, 77 | [KEY_F4] = 0x3d, 78 | [KEY_F5] = 0x3e, 79 | [KEY_F6] = 0x3f, 80 | [KEY_F7] = 0x40, 81 | [KEY_F8] = 0x41, 82 | [KEY_F9] = 0x42, 83 | [KEY_F10] = 0x43, 84 | [KEY_F11] = 0x44, 85 | [KEY_F12] = 0x45, 86 | [KEY_INSERT] = 0x49, 87 | [KEY_HOME] = 0x4a, 88 | [KEY_PAGEUP] = 0x4b, 89 | [KEY_DELETE] = 0x4c, 90 | [KEY_END] = 0x4d, 91 | [KEY_PAGEDOWN] = 0x4e, 92 | [KEY_RIGHT] = 0x4f, 93 | [KEY_LEFT] = 0x50, 94 | [KEY_DOWN] = 0x51, 95 | [KEY_UP] = 0x52, 96 | [KEY_NUMLOCK] = 0x53, 97 | [KEY_102ND] = 0x64, 98 | [KEY_SYSRQ] = 0, 99 | }; 100 | 101 | int main(int argc, char **argv) { 102 | struct epoll_event events[MAX_EVENTS]; 103 | int fd = open("/dev/input/event0", O_RDWR); 104 | assert(fd >= 0); 105 | int ret = ioctl(fd, EVIOCGRAB, (void*)1); 106 | assert(ret == 0); 107 | 108 | int epollfd = epoll_create1(0); 109 | assert(epollfd >= 0); 110 | 111 | struct epoll_event ev; 112 | ev.events = EPOLLIN; 113 | ev.data.fd = fd; 114 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) { 115 | perror("unable to epoll add"); 116 | return 1; 117 | } 118 | 119 | int hidout = open("/dev/hidg0", O_RDWR); 120 | assert(hidout >= 0); 121 | uint8_t report[8]; 122 | printf("hidout: %d\n", hidout); 123 | 124 | uint8_t modifiers = 0; 125 | 126 | while (1) { 127 | int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); 128 | if (nfds < 0) { 129 | printf("%d ", nfds); 130 | perror("epoll_wait failed"); 131 | return 2; 132 | } 133 | int keys = 0; 134 | memset(report, 0, 8); 135 | for (int i=0; i < nfds; i++) { 136 | struct input_event ev[64]; 137 | int rd = read(events[i].data.fd, ev, sizeof(ev)); 138 | assert(rd > ((signed int)sizeof(struct input_event))); 139 | for (int j=0; j < rd / ((signed int)sizeof(struct input_event)); j++) { 140 | unsigned int type, code; 141 | type = ev[j].type; 142 | code = ev[j].code; 143 | //printf("Event: time %ld.%06ld, ", ev[j].time.tv_sec, ev[j].time.tv_usec); 144 | if (type == EV_KEY) { 145 | if (key_mappings[code] == 0) { 146 | printf("code %d %d report:%x\n", code, ev[j].value, key_mappings[code]); 147 | } 148 | uint8_t mod = 0; 149 | switch (code) { 150 | case KEY_LEFTCTRL: 151 | mod = 0x01; 152 | break; 153 | case KEY_RIGHTCTRL: 154 | mod = 0x10; 155 | break; 156 | case KEY_LEFTSHIFT: 157 | mod = 0x02; 158 | break; 159 | case KEY_RIGHTSHIFT: 160 | mod = 0x20; 161 | break; 162 | case KEY_LEFTALT: 163 | mod = 0x04; 164 | break; 165 | case KEY_RIGHTALT: 166 | mod = 0x40; 167 | break; 168 | case KEY_LEFTMETA: 169 | mod = 0x08; 170 | break; 171 | } 172 | if (ev[j].value) { 173 | modifiers |= mod; 174 | } else { 175 | modifiers &= ~mod; 176 | } 177 | report[0] = modifiers; 178 | if (ev[j].value) { 179 | if (keys < 6) { 180 | report[2+keys] = key_mappings[code]; 181 | keys++; 182 | } 183 | } 184 | } 185 | } 186 | } 187 | int wr = write(hidout, report, 8); 188 | if (wr != 8) { 189 | perror("problem writing to hidout"); 190 | } 191 | //puts(""); 192 | } 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /nix/android-auto.nix: -------------------------------------------------------------------------------- 1 | pself: psuper: { 2 | android-auto = pself.extend (self: super: { 3 | android-auto = self.callPackage ../android-auto {}; 4 | initrd_script = '' 5 | modprobe -v dwc2 6 | modprobe -v gadgetfs 7 | mkdir /dev/gadget 8 | mount -t gadgetfs gadgetfs /dev/gadget 9 | 10 | openssl req -out test.x509 -newkey 2048 -keyout test.key -nodes -x509 -days 365 -subj '/commonName=testname/' 11 | export AAKEY=$(realpath test.key) 12 | export AACERT=$(realpath test.x509) 13 | android-auto 14 | ''; 15 | scripts = self.runCommand "scripts" {} '' 16 | mkdir -pv $out/bin 17 | cp ${../android-auto/custom-gadget} $out/bin/custom-gadget 18 | ''; 19 | extra_utils = [ self.android-auto self.openssl self.scripts ]; 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /nix/common.nix: -------------------------------------------------------------------------------- 1 | let 2 | sources = import ./sources.nix; 3 | nativePkgs = import sources.nixpkgs {}; 4 | master = import (builtins.fetchTarball https://github.com/nixos/nixpkgs/archive/master.tar.gz) {}; 5 | in 6 | self: super: { 7 | inherit sources; 8 | baseFirmware = self.runCommand "base-firmware" {} '' 9 | mkdir $out 10 | cp -r ${self.sources.firmware}/boot/{*.dtb,kernel*img,fixup*dat,start*elf,overlays} $out/ 11 | ''; 12 | modulesForKernel = kernel: self.runCommand "modules" {} '' 13 | mkdir -pv $out/lib/modules/ 14 | cp -r ${self.sources.firmware}/modules/${kernel} $out/lib/modules/ 15 | ''; 16 | libftdi = null; 17 | openocd = super.openocd.overrideAttrs (old: { 18 | src = self.fetchFromGitHub { 19 | owner = "raspberrypi"; 20 | repo = "openocd"; 21 | rev = "14c0d0d330bd6b2cdc0605ee9a9256e5627a905e"; 22 | fetchSubmodules = true; 23 | sha256 = "sha256-o7shTToj6K37Xw+Crwif5WwB4GfPYIiMJ/o/9u3xrsE="; 24 | }; 25 | nativeBuildInputs = old.nativeBuildInputs ++ [ nativePkgs.autoreconfHook nativePkgs.gcc ]; 26 | #buildInputs = old.buildInputs ++ [ self.tcl ]; 27 | preConfigure = '' 28 | pwd 29 | ls -l 30 | ''; 31 | configureFlags = [ 32 | "--enable-bcm2835gpio" 33 | "--enable-sysfsgpio" 34 | ]; 35 | }); 36 | shrunken_busybox = self.runCommand "shrunk-busybox" { 37 | busybox = self.busybox.override { enableStatic=true; }; 38 | nativeBuildInputs = [ self.buildPackages.nukeReferences ]; 39 | } '' 40 | mkdir $out 41 | cp -vir $busybox/bin $out/ 42 | chmod +w $out/bin 43 | chmod +w $out/bin/busybox 44 | nuke-refs $out/bin/busybox 45 | ''; 46 | boottime = self.stdenv.mkDerivation { 47 | name = "boottime"; 48 | unpackPhase = '' 49 | cp ${../boottime.c} boottime.c 50 | export sourceRoot=. 51 | ''; 52 | buildPhase = '' 53 | $CC boottime.c -o boottime 54 | ''; 55 | installPhase = '' 56 | mkdir -p $out/bin 57 | cp boottime $out/bin/ 58 | ''; 59 | }; 60 | rpi-tools = self.lib.makeScope self.newScope (iself: { 61 | utils = iself.callPackage ../utils {}; 62 | tlsf = null; 63 | common = iself.callPackage "${self.sources.rpi-open-firmware}/common" {}; 64 | }); 65 | withWifi = false; 66 | etc = self.runCommand "etc" { 67 | nsswitch = '' 68 | passwd: files systemd 69 | group: files systemd 70 | shadow: files 71 | 72 | hosts: files mymachines mdns_minimal [NOTFOUND=return] dns mdns myhostname 73 | networks: files 74 | 75 | ethers: files 76 | services: files 77 | protocols: files 78 | rpc: files 79 | ''; 80 | # sets root password to password 81 | passwd = '' 82 | root:nxz2xIegZ0Ytc:0:0:System administrator:/:/bin/sh 83 | avahi:x:10:10:avahi-daemon privilege separation user:/var/empty:/run/current-system/sw/bin/nologin 84 | sshd:x:498:65534:SSH privilege separation user:/var/empty:/run/current-system/sw/bin/nologin 85 | nscd:x:2:2:nscd privilege separation user:/var/empty:/run/current-system/sw/bin/nologin 86 | ''; 87 | group = '' 88 | avahi:x:10: 89 | ''; 90 | sshd_config = '' 91 | UsePAM no 92 | Port 22 93 | ''; 94 | nscd = '' 95 | server-user nscd 96 | 97 | enable-cache passwd yes 98 | positive-time-to-live passwd 0 99 | negative-time-to-live passwd 0 100 | shared passwd yes 101 | 102 | enable-cache group yes 103 | positive-time-to-live group 0 104 | negative-time-to-live group 0 105 | shared group yes 106 | 107 | enable-cache netgroup yes 108 | positive-time-to-live netgroup 0 109 | negative-time-to-live netgroup 0 110 | shared netgroup yes 111 | 112 | enable-cache hosts yes 113 | positive-time-to-live hosts 0 114 | negative-time-to-live hosts 0 115 | shared hosts yes 116 | 117 | enable-cache services yes 118 | positive-time-to-live services 0 119 | negative-time-to-live services 0 120 | shared services yes 121 | ''; 122 | passAsFile = [ "nsswitch" "passwd" "sshd_config" "nscd" "group" ]; 123 | nativeBuildInputs = [ nativePkgs.nukeReferences ]; 124 | } '' 125 | mkdir -p $out/ssh 126 | cd $out 127 | ${self.lib.optionalString self.withWifi '' 128 | cp ${../wpa_supplicant.conf} wpa_supplicant.conf 129 | ''} 130 | cp -r ${self.avahi}/etc/avahi avahi 131 | chmod +w -R avahi 132 | for x in avahi/avahi-autoipd.action avahi/avahi-dnsconfd.action; do 133 | nuke-refs $x 134 | done 135 | cp $nsswitchPath nsswitch.conf 136 | cp $passwdPath passwd 137 | cp $groupPath group 138 | cp $sshd_configPath ssh/sshd_config 139 | cp $nscdPath nscd.conf 140 | ''; 141 | # 5.4.72-v7l+ 142 | moduleClosureForKernel = kernel: self.makeModulesClosure { 143 | kernel = self.modulesForKernel kernel; 144 | firmware = self.buildEnv { 145 | name = "all-the-firmware"; 146 | paths = [ self.firmwareLinuxNonfree master.raspberrypiWirelessFirmware ]; 147 | ignoreCollisions = true; 148 | }; 149 | allowMissing = true; 150 | rootModules = [ 151 | "dwc2" 152 | "usb_f_acm" 153 | "usb_f_rndis" 154 | "usb_f_mass_storage" 155 | "gadgetfs" # for custom userland gadgets 156 | "usb_f_hid" 157 | "usb_f_rndis" 158 | ] ++ self.extra_modules; 159 | }; 160 | extra_modules = []; 161 | installedPackages = self.buildEnv { 162 | name = "bin"; 163 | paths = [ 164 | self.shrunken_busybox 165 | #self.boottime 166 | #self.gdb 167 | self.shrunkenPackages 168 | ] ++ self.extra_utils; 169 | }; 170 | libnl = super.libnl.override { pythonSupport = false; }; 171 | shrunkenPackages = self.runCommandCC "shrunken-packages" { nativeBuildInputs = [ nativePkgs.nukeReferences ]; } '' 172 | mkdir -p $out/{bin,lib} 173 | #cp {self.openocd}/bin/openocd $out/bin 174 | cp ${self.wpa_supplicant}/bin/wpa_supplicant $out/bin 175 | cp ${self.avahi}/bin/avahi-daemon $out/bin 176 | cp ${self.strace}/bin/strace $out/bin 177 | cp ${self.openssh}/bin/sshd $out/bin 178 | #cp {self.iproute}/bin/ip $out/bin 179 | cp ${self.dropbear}/bin/dropbear $out/bin/ 180 | cp ${self.glibcCross.bin}/bin/nscd $out/bin 181 | #cp {self.smi-test}/bin/smi-test $out/bin 182 | 183 | cp ${self.hidapi}/lib/libhidapi-hidraw.so.0 $out/lib 184 | cp ${self.libusb1}/lib/libusb-1.0.so.0 $out/lib 185 | cp ${self.glibcCross}/lib/lib{m.so.6,dl.so.2,pthread.so.0,c.so.6,rt.so.1,util.so.1,crypt.so.1,resolv.so.2,nss_files.so.2} $out/lib 186 | cp ${self.udev.lib}/lib/libudev.so.1 $out/lib 187 | cp $(cat $(cat $NIX_CC/nix-support/orig-cc)/nix-support/propagated-build-inputs)/lib/lib{gcc_s.so.1,ssp.so.0} $out/lib/ 188 | cp ${self.utillinux.out}/lib/lib{mount.so.1,blkid.so.1,uuid.so.1} $out/lib/ 189 | cp ${self.openssl.out}/lib/lib{ssl.so.1.0.0,crypto.so.1.0.0} $out/lib 190 | cp ${self.libnl.out}/lib/li{bnl-3.so.200,bnl-genl-3.so.200} $out/lib 191 | cp ${self.pcsclite.out}/lib/libpcsclite.so.1 $out/lib 192 | cp ${self.dbus.lib}/lib/libdbus-1.so.3 $out/lib 193 | cp ${self.systemd.lib}/lib/libsystemd.so.0 $out/lib 194 | cp ${self.xz.out}/lib/liblzma.so.5 $out/lib 195 | cp ${self.lz4.out}/lib/liblz4.so.1 $out/lib 196 | cp ${self.libcap.lib}/lib/libcap.so.2 $out/lib 197 | cp ${self.libgcrypt.out}/lib/libgcrypt.so.20 $out/lib 198 | cp ${self.libgpgerror}/lib/libgpg-error.so.0 $out/lib 199 | cp ${self.avahi}/lib/lib{avahi-common.so.3,avahi-core.so.7} $out/lib 200 | cp ${self.libdaemon}/lib/libdaemon.so.0 $out/lib 201 | cp ${self.expat}/lib/libexpat.so.1 $out/lib 202 | cp ${self.libunwind}/lib/lib{unwind-ptrace.so.0,unwind-arm.so.8,unwind.so.8} $out/lib 203 | cp ${self.pam}/lib/libpam.so.0 $out/lib 204 | cp ${self.zlib}/lib/libz.so.1 $out/lib 205 | cp ${self.libkrb5}/lib/lib{gssapi_krb5.so.2,krb5.so.3,k5crypto.so.3,com_err.so.3,krb5support.so.0} $out/lib 206 | cp ${self.keyutils.lib}/lib/libkeyutils.so.1 $out/lib 207 | 208 | linker=$(basename $(cat $NIX_CC/nix-support/dynamic-linker)) 209 | chmod +w -R $out 210 | 211 | #sed -i -e 's@{self.openocd}@/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-{self.openocd.name}@' $out/bin/openocd 212 | #sed -i -e 's@{self.openocd}@/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-{self.openocd.name}@' $out/bin/openocd 213 | for x in $out/lib/lib{pthread.so.0,gcc_s.so.1,rt.so.1,mount.so.1,crypto.so.1.0.0,dbus-1.so.3,gpg-error.so.0,ssp.so.0,daemon.so.0,avahi-common.so.3,pam.so.0,util.so.1,z.so.1,crypt.so.1,resolv.so.2,gssapi_krb5.so.2,krb5.so.3,k5crypto.so.3,com_err.so.3,nss_files.so.2}; do 214 | nuke-refs $x 215 | done 216 | nuke-refs $out/bin/avahi-daemon 217 | nuke-refs $out/bin/sshd 218 | nuke-refs $out/bin/nscd 219 | 220 | for bin in $out/bin/*; do 221 | patchelf --set-rpath $out/lib --set-interpreter $out/lib/$linker $bin 222 | done 223 | for lib in $out/lib/*; do 224 | patchelf --set-rpath $out/lib $lib 225 | done 226 | 227 | cp $(cat $NIX_CC/nix-support/dynamic-linker) $out/lib/$linker 228 | chmod +w -R $out 229 | $STRIP $out/lib/$linker 230 | nuke-refs $out/lib/libc.so* 231 | nuke-refs $out/lib/libdl.so.2 232 | nuke-refs $out/lib/libm.so.6 233 | sed -i -e 's@${self.glibcCross.out}@/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-${self.glibcCross.name}@' $out/lib/$linker 234 | ''; 235 | trimRootDir = '' 236 | rm kernel8.img kernel7.img kernel.img 237 | rm start.elf start*db.elf start*x.elf start*cd.elf 238 | rm fixup.dat fixup*db.dat fixup*x.dat fixup*cd.dat 239 | rm bcm{2708,2710,2709}*dtb 240 | ''; 241 | kernel_version = "5.4.72"; 242 | kernelVersionList = [ 243 | "+" # pi0 244 | "-v7+" 245 | "-v7l+" 246 | "-v8+" 247 | ]; 248 | kernel_versions = map (x: "${self.kernel_version}${x}") self.kernelVersionList; 249 | modulesForKernels = self.buildEnv { 250 | name = "all-the-modules"; 251 | paths = (map self.moduleClosureForKernel self.kernel_versions) ++ [ self.wireless-regdb self.raspberrypiWirelessFirmware ]; 252 | pathsToLink = [ "/lib" ]; 253 | ignoreCollisions = true; 254 | }; 255 | initrd = self.makeInitrd { 256 | contents = [ 257 | { 258 | symlink = "/init"; 259 | object = self.initScript; 260 | } 261 | { 262 | symlink = "/lib/modules"; 263 | object = "${self.modulesForKernels}/lib/modules"; 264 | } 265 | { 266 | symlink = "/lib/firmware"; 267 | object = "${self.modulesForKernels}/lib/firmware"; 268 | } 269 | { 270 | symlink = "/bin"; 271 | object = "${self.installedPackages}/bin"; 272 | } 273 | { 274 | symlink = "/etc"; 275 | object = self.etc; 276 | } 277 | ]; 278 | }; 279 | closure = self.runCommand "closure-helper" {} '' 280 | mkdir $out 281 | cd $out 282 | ln -s ${self.initScript} init 283 | ln -s ${self.modulesForKernels} modules 284 | ln -s ${self.installedPackages} bin 285 | ln -s ${self.etc} etc 286 | ''; 287 | # see also: 288 | # https://elinux.org/images/e/ef/USB_Gadget_Configfs_API_0.pdf 289 | initScript = self.writeTextFile { 290 | name = "init"; 291 | text = '' 292 | #!/bin/ash 293 | 294 | mount -t proc proc proc 295 | mount -t sysfs sys sys 296 | mount -t devtmpfs dev dev 297 | mount -t configfs none /sys/kernel/config 298 | mkdir /dev/pts 299 | mount -t devpts devpts /dev/pts 300 | mount -t debugfs debugfs /sys/kernel/debug 301 | 302 | boottime 303 | 304 | depmod 305 | serial=$(cut -c9-16 < /proc/device-tree/serial-number) 306 | hostname pi-''${serial} 307 | 308 | ${self.initrd_script} 309 | 310 | boottime 311 | 312 | exec ash 313 | ''; 314 | executable = true; 315 | }; 316 | custom-overlays = self.callPackage ({ runCommand, dtc, }: runCommand "custom-overlays" { nativeBuildInputs = [ dtc ]; } '' 317 | mkdir -p $out/overlays 318 | cd $out/overlays 319 | dtc -@ -Hepapr -I dts -O dtb -o smi-speed.dtbo ${../smi-speed-overlay.dts} 320 | '') {}; 321 | firmware-with-custom-overlays = self.buildEnv { 322 | name = "firmware-with-custom-overlays"; 323 | paths = [ self.baseFirmware self.custom-overlays ]; 324 | }; 325 | rootDir = self.runCommand "rootdir" {} '' 326 | mkdir $out 327 | cd $out 328 | ln -s ${self.firmware-with-custom-overlays}/* . 329 | ln -s ${self.initrd}/initrd initrd 330 | ls -lLhs initrd 331 | cat < config.txt 332 | dtoverlay=dwc2 333 | dtoverlay=smi 334 | dtoverlay=smi-dev 335 | dtoverlay=smi-speed 336 | dtparam=axiperf 337 | enable_uart=1 338 | uart_2ndstage=1 339 | dtoverlay=disable-bt 340 | 341 | initramfs initrd followkernel 342 | EOF 343 | 344 | cat < cmdline.txt 345 | nada console=tty1 console=serial0,115200 346 | EOF 347 | 348 | ${self.trimRootDir} 349 | ''; 350 | util = self.runCommand "util" {} '' 351 | mkdir $out 352 | cd $out 353 | ln -sv ${self.rootDir} rootdir 354 | ln -sv ${self.closure} closure 355 | ''; 356 | util2 = (import {system="aarch64-linux";}).runCommand "util2" {} '' 357 | ${self.shrunkenPackages}/bin/strace --help 358 | touch $out 359 | ''; 360 | rootZip = self.runCommand "rootzip" { nativeBuildInputs = [ self.buildPackages.zip ]; } '' 361 | cd ${self.rootDir} 362 | mkdir $out 363 | zip -r $out/root.zip * 364 | cd $out 365 | mkdir nix-support 366 | echo "file binary-dist $out/root.zip" > nix-support/hydra-build-products 367 | ''; 368 | } 369 | -------------------------------------------------------------------------------- /nix/keyboard.nix: -------------------------------------------------------------------------------- 1 | pself: psuper: { 2 | keyboard = pself.extend (self: super: { 3 | sendkey = self.writeScriptBin "sendkey" '' 4 | #!/bin/ash 5 | echo -ne \\x00\\x00\\x''${1}\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00 > /dev/hidg0 6 | ''; 7 | keyboard_proxy = self.stdenv.mkDerivation { 8 | name = "keyboard-proxy"; 9 | src = ../keyboard-proxy; 10 | buildPhase = '' 11 | $CC main.c -o keyboard-proxy -Wall 12 | ''; 13 | installPhase = '' 14 | mkdir -pv $out/bin/ 15 | cp keyboard-proxy $out/bin/ 16 | ''; 17 | }; 18 | extra_utils = [ self.sendkey self.keyboard_proxy ]; 19 | initrd_script = '' 20 | modprobe -v dwc2 21 | modprobe usb_f_hid 22 | 23 | cd /sys/kernel/config/usb_gadget 24 | mkdir g1 25 | cd g1 26 | 27 | mkdir functions/hid.foo 28 | cd functions/hid.foo 29 | echo 1 > protocol 30 | echo 1 > subclass 31 | echo 8 > report_length 32 | echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > report_desc 33 | cd ../.. 34 | 35 | mkdir configs/c.1 36 | mkdir configs/c.1/strings/0x409 37 | echo "keyboard mode" > configs/c.1/strings/0x409/configuration 38 | 39 | mkdir strings/0x409 40 | echo "cleverca22" > strings/0x409/manufacturer 41 | echo "rpi-tools pi400 keyboard" > strings/0x409/product 42 | grep Serial /proc/cpuinfo | cut -c19-26 > strings/0x409/serialnumber 43 | 44 | ln -sv functions/hid.foo configs/c.1 45 | 46 | echo fe980000.usb > UDC 47 | 48 | cd / 49 | sleep 5 50 | keyboard-proxy & 51 | ''; 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /nix/msd.nix: -------------------------------------------------------------------------------- 1 | pself: psuper: { 2 | msd = pself.extend (self: super: { 3 | extra_utils = [ ]; 4 | initrd_script = '' 5 | truncate -s $((1024*1024*64)) disk.img 6 | modprobe -v dwc2 7 | modprobe -v usb_f_acm 8 | modprobe -v usb_f_mass_storage 9 | modprobe -v usb_f_rndis 10 | 11 | cd /sys/kernel/config/usb_gadget 12 | mkdir g1 13 | cd g1 14 | 15 | mkdir functions/acm.GS0 16 | 17 | mkdir functions/mass_storage.GS0 18 | echo 1 > functions/mass_storage.GS0/lun.0/removable 19 | if [ -e /dev/mmcblk0 ]; then 20 | echo /dev/mmcblk0 > functions/mass_storage.GS0/lun.0/file 21 | else 22 | echo /disk.img > functions/mass_storage.GS0/lun.0/file 23 | fi 24 | 25 | mkdir functions/rndis.GS0 26 | 27 | mkdir configs/c.1 28 | mkdir configs/c.1/strings/0x409 29 | echo "Serial Console + MSD" > configs/c.1/strings/0x409/configuration 30 | 31 | mkdir strings/0x409 32 | echo "cleverca22" > strings/0x409/manufacturer 33 | echo "rpi-tools" > strings/0x409/product 34 | grep Serial /proc/cpuinfo | cut -c19-26 > strings/0x409/serialnumber 35 | 36 | ln -sv functions/acm.GS0 configs/c.1 37 | #ln -sv functions/mass_storage.GS0 configs/c.1 38 | ln -sv functions/rndis.GS0 configs/c.1 39 | 40 | echo fe980000.usb > UDC 41 | 42 | cd / 43 | getty 0 /dev/ttyGS0 & 44 | sleep 5 45 | ''; 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /nix/pi0-swd.nix: -------------------------------------------------------------------------------- 1 | pself: psuper: { 2 | pi0-swd = (import pself.path { overlays = [ (import ./common.nix) ]; }).pkgsCross.raspberryPi.extend (self: super: { 3 | initrd_script = '' 4 | modprobe brcmfmac 5 | modprobe bcm2835_smi_dev 6 | sleep 10 7 | wpa_supplicant -iwlan0 -Dnl80211,wext -c /etc/wpa_supplicant.conf & 8 | sleep 5 9 | ip addr add 192.168.2.50 dev wlan0 10 | ip route add 192.168.2.0/24 dev wlan0 11 | mkdir -pv /etc/dropbear /var/log 12 | touch /var/log/lastlog 13 | /bin/dropbear -R -E & 14 | mkdir -pv /var/run/nscd 15 | LD_LIBRARY_PATH=${self.shrunkenPackages}/lib nscd & 16 | mkdir -p /run/avahi-daemon 17 | ''; 18 | #systemd = self.eudev; 19 | #systemdMinimal = null; 20 | #libusb = super.libusb.override { enableUdev = false; }; 21 | #pcsclite = super.pcsclite.overrideAttrs (old: { configureFlags = old.configureFlags ++ [ "--disable-libsystemd" "--disable-libudev" ]; }); 22 | wpa_supplicant = super.wpa_supplicant.overrideAttrs (old: { 23 | buildInputs = old.buildInputs ++ [ self.libusb ]; 24 | }); 25 | ocd-scripts = self.runCommand "ocd-scripts" {} '' 26 | cp -vir ${self.openocd}/share/openocd/scripts/ $out 27 | ''; 28 | startOcd = self.writeScriptBin "start-ocd" '' 29 | #!/bin/sh 30 | openocd -f interface/raspberrypi-swd.cfg -f target/rp2040.cfg -s ${self.ocd-scripts} 31 | ''; 32 | extra_utils = with self; [ 33 | #wpa_supplicant 34 | #openocd 35 | self.startOcd 36 | ]; 37 | trimRootDir = '' 38 | rm start4* 39 | rm fixup4* 40 | rm bcm2711* bcm2710* bcm2709* 41 | rm kernel8.img kernel7l.img kernel7.img 42 | ''; 43 | extra_modules = [ 44 | "brcmfmac" 45 | "configs" 46 | "bcm2835_smi" 47 | "bcm2835_smi_dev" 48 | "raspberrypi_axi_monitor" 49 | ]; 50 | kernelVersionList = [ "+" ]; 51 | smi-test = self.stdenv.mkDerivation { 52 | name = "smi-test"; 53 | unpackPhase = '' 54 | cp ${../smi-test.c} smi-test.c 55 | export sourceRoot=. 56 | ''; 57 | buildPhase = '' 58 | $CC smi-test.c -o smi-test -I ${self.linux_rpi.src}/include/ 59 | ''; 60 | installPhase = '' 61 | mkdir -p $out/bin 62 | cp smi-test $out/bin 63 | ''; 64 | }; 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /nix/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "firmware": { 3 | "branch": "master", 4 | "description": "This repository contains pre-compiled binaries of the current Raspberry Pi kernel and modules, userspace libraries, and bootloader/GPU firmware.", 5 | "homepage": "", 6 | "owner": "raspberrypi", 7 | "repo": "firmware", 8 | "rev": "5f95dfda0edaac1a353400915a92bcc61008c780", 9 | "sha256": "0j5m50cmmr11m3h8kk89j1pqkdqr7mzdzg04ayiqvfhvy32qqlg8", 10 | "type": "tarball", 11 | "url": "https://github.com/raspberrypi/firmware/archive/5f95dfda0edaac1a353400915a92bcc61008c780.tar.gz", 12 | "url_template": "https://github.com///archive/.tar.gz" 13 | }, 14 | "niv": { 15 | "branch": "master", 16 | "description": "Easy dependency management for Nix projects", 17 | "homepage": "https://github.com/nmattia/niv", 18 | "owner": "nmattia", 19 | "repo": "niv", 20 | "rev": "29ddaaf4e099c3ac0647f5b652469dfc79cd3b53", 21 | "sha256": "1va6myp07gkspgxfch8z3rs9nyvys6jmgzkys6a2c4j09qxp1bs0", 22 | "type": "tarball", 23 | "url": "https://github.com/nmattia/niv/archive/29ddaaf4e099c3ac0647f5b652469dfc79cd3b53.tar.gz", 24 | "url_template": "https://github.com///archive/.tar.gz" 25 | }, 26 | "nixpkgs": { 27 | "branch": "nixos-19.03", 28 | "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to", 29 | "homepage": "https://github.com/NixOS/nixpkgs", 30 | "owner": "nixos", 31 | "repo": "nixpkgs-channels", 32 | "rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59", 33 | "sha256": "11z6ajj108fy2q5g8y4higlcaqncrbjm3dnv17pvif6avagw4mcb", 34 | "type": "tarball", 35 | "url": "https://github.com/nixos/nixpkgs-channels/archive/34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59.tar.gz", 36 | "url_template": "https://github.com///archive/.tar.gz" 37 | }, 38 | "rpi-eeprom": { 39 | "branch": "master", 40 | "description": "Installation scripts and binaries for the closed sourced Raspberry Pi 4 EEPROMs", 41 | "homepage": "https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md", 42 | "owner": "raspberrypi", 43 | "repo": "rpi-eeprom", 44 | "rev": "54a9796abbee59067bff9da6b90c1014178f2c21", 45 | "sha256": "0yp7bn444n6yisp4hiblrm00rrvrf213amzb4sh96mlb5nhxspqk", 46 | "type": "tarball", 47 | "url": "https://github.com/raspberrypi/rpi-eeprom/archive/54a9796abbee59067bff9da6b90c1014178f2c21.tar.gz", 48 | "url_template": "https://github.com///archive/.tar.gz" 49 | }, 50 | "rpi-open-firmware": { 51 | "branch": "master", 52 | "description": "Open source VPU side bootloader for Raspberry Pi.", 53 | "homepage": null, 54 | "owner": "librerpi", 55 | "repo": "rpi-open-firmware", 56 | "rev": "059d2e0fc19e2d0f30bb74d42a82a2654f498f2d", 57 | "sha256": "0crl6qgw69xs8a8lsp82k0c3rc1vgvkynp2if6lwhzxzsbw7zqbd", 58 | "type": "tarball", 59 | "url": "https://github.com/librerpi/rpi-open-firmware/archive/059d2e0fc19e2d0f30bb74d42a82a2654f498f2d.tar.gz", 60 | "url_template": "https://github.com///archive/.tar.gz" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /nix/sources.nix: -------------------------------------------------------------------------------- 1 | # This file has been generated by Niv. 2 | 3 | # A record, from name to path, of the third-party packages 4 | with rec 5 | { 6 | pkgs = 7 | if hasNixpkgsPath 8 | then 9 | if hasThisAsNixpkgsPath 10 | then import (builtins_fetchTarball { inherit (sources_nixpkgs) url sha256; }) {} 11 | else import {} 12 | else 13 | import (builtins_fetchTarball { inherit (sources_nixpkgs) url sha256; }) {}; 14 | 15 | sources_nixpkgs = 16 | if builtins.hasAttr "nixpkgs" sources 17 | then sources.nixpkgs 18 | else abort 19 | '' 20 | Please specify either (through -I or NIX_PATH=nixpkgs=...) or 21 | add a package called "nixpkgs" to your sources.json. 22 | ''; 23 | 24 | # fetchTarball version that is compatible between all the versions of Nix 25 | builtins_fetchTarball = 26 | { url, sha256 }@attrs: 27 | let 28 | inherit (builtins) lessThan nixVersion fetchTarball; 29 | in 30 | if lessThan nixVersion "1.12" then 31 | fetchTarball { inherit url; } 32 | else 33 | fetchTarball attrs; 34 | 35 | # fetchurl version that is compatible between all the versions of Nix 36 | builtins_fetchurl = 37 | { url, sha256 }@attrs: 38 | let 39 | inherit (builtins) lessThan nixVersion fetchurl; 40 | in 41 | if lessThan nixVersion "1.12" then 42 | fetchurl { inherit url; } 43 | else 44 | fetchurl attrs; 45 | 46 | # A wrapper around pkgs.fetchzip that has inspectable arguments, 47 | # annoyingly this means we have to specify them 48 | fetchzip = { url, sha256 }@attrs: pkgs.fetchzip attrs; 49 | 50 | # A wrapper around pkgs.fetchurl that has inspectable arguments, 51 | # annoyingly this means we have to specify them 52 | fetchurl = { url, sha256 }@attrs: pkgs.fetchurl attrs; 53 | 54 | hasNixpkgsPath = (builtins.tryEval ).success; 55 | hasThisAsNixpkgsPath = 56 | (builtins.tryEval ).success && == ./.; 57 | 58 | sources = builtins.fromJSON (builtins.readFile ./sources.json); 59 | 60 | mapAttrs = builtins.mapAttrs or 61 | (f: set: with builtins; 62 | listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))); 63 | 64 | # borrowed from nixpkgs 65 | functionArgs = f: f.__functionArgs or (builtins.functionArgs f); 66 | callFunctionWith = autoArgs: f: args: 67 | let auto = builtins.intersectAttrs (functionArgs f) autoArgs; 68 | in f (auto // args); 69 | 70 | getFetcher = spec: 71 | let fetcherName = 72 | if builtins.hasAttr "type" spec 73 | then builtins.getAttr "type" spec 74 | else "builtin-tarball"; 75 | in builtins.getAttr fetcherName { 76 | "tarball" = fetchzip; 77 | "builtin-tarball" = builtins_fetchTarball; 78 | "file" = fetchurl; 79 | "builtin-url" = builtins_fetchurl; 80 | }; 81 | }; 82 | # NOTE: spec must _not_ have an "outPath" attribute 83 | mapAttrs (_: spec: 84 | if builtins.hasAttr "outPath" spec 85 | then abort 86 | "The values in sources.json should not have an 'outPath' attribute" 87 | else 88 | if builtins.hasAttr "url" spec && builtins.hasAttr "sha256" spec 89 | then 90 | spec // 91 | { outPath = callFunctionWith spec (getFetcher spec) { }; } 92 | else spec 93 | ) sources 94 | -------------------------------------------------------------------------------- /pi5_voltage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from http.server import HTTPServer, BaseHTTPRequestHandler 4 | from subprocess import run 5 | import re 6 | 7 | fmt = 'pi5_{}{{name="{}",id="{}"}} {}\n' 8 | fmt2 = 'pi5_clock{{name="{}"}} {}\n' 9 | 10 | def get_clock(name): 11 | res = run(["vcgencmd","measure_clock",name], capture_output=True) 12 | res = re.search('=([0-9]+)', res.stdout.decode("utf-8")) 13 | return res.group(1) 14 | 15 | class MyHandler(BaseHTTPRequestHandler): 16 | def do_GET(self): 17 | self.send_response(200) 18 | self.send_header("Content-type", "text/html") 19 | self.end_headers() 20 | res = run(["vcgencmd","pmic_read_adc"], capture_output=True) 21 | lines = res.stdout.decode("utf-8").splitlines() 22 | for line in lines: 23 | res = re.search('([A-Z_0-9]+)_[VA] (current|volt)\(([0-9]+)\)=([0-9.]+)', line) 24 | self.wfile.write(fmt.format(res.group(2), res.group(1), res.group(3), res.group(4)).encode("utf-8")) 25 | clocks = [ "arm", "plla", "pllb", "pllc", "uart", "core" ] 26 | for clock in clocks: 27 | self.wfile.write(fmt2.format(clock, get_clock(clock)).encode("utf-8")) 28 | 29 | httpd = HTTPServer(('', 9101), MyHandler) 30 | httpd.serve_forever() 31 | -------------------------------------------------------------------------------- /release.nix: -------------------------------------------------------------------------------- 1 | let 2 | self = import ./.; 3 | in { 4 | pi400_keyboard = self.pkgsCross.armv7l-hf-multiplatform.pkgsStatic.keyboard.rootZip; 5 | bcm2711_msd = self.pkgsCross.armv7l-hf-multiplatform.pkgsStatic.msd.rootZip; 6 | } 7 | -------------------------------------------------------------------------------- /repl.nix: -------------------------------------------------------------------------------- 1 | builtins.getFlake (toString ./.) 2 | -------------------------------------------------------------------------------- /signing-tool/bootcode.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | var crypto = require("crypto"); 3 | 4 | function parse_bootcode(full_bootcode) { 5 | const footer = full_bootcode.slice(full_bootcode.length - 284); 6 | 7 | const payload_size = footer.readUint32LE(0); 8 | console.log("payload size:", payload_size); 9 | if ((payload_size + 284) == full_bootcode.length) { 10 | console.log("payload size is valid"); 11 | } 12 | 13 | const key_index = footer.readUint32LE(4); 14 | console.log("key index:", key_index); 15 | 16 | const rsa_signature = footer.slice(8, 264); 17 | console.log("rsa sig:", rsa_signature.toString("hex")); 18 | // from both https://github.com/librerpi/rpi-open-firmware/commit/a65026a501d2e3e5b7c5397ad377b2acb27b795b and a discord convo with another person 19 | // The type-00 blob is signed with RSA-SHA1 with PKCS#1 v1.5 padding (signature goes just before the HMAC and is covered by it). The public key is 20 | // The RSA signature is interpreted as a 2048-bit big-endian integer, which is decrypted using the exponent value of 65537 and one of the 4 public key modulos and exporting the result again as a 2048-bit big-endian integer. 21 | 22 | try { 23 | const pubkey_pem = fs.readFileSync(process.mainModule.path + "/pubkey" + key_index + ".pem"); 24 | const pubkey = crypto.createPublicKey(pubkey_pem); 25 | 26 | var verify = crypto.createVerify("RSA-SHA1"); 27 | verify.update(full_bootcode.slice(0, payload_size + 8)); 28 | console.log("is rsa sig valid:", verify.verify(pubkey, rsa_signature)); 29 | } catch (e) { 30 | console.log("error loading pubkey for slot "+key_index, e); 31 | } 32 | 33 | const hmac_sig = footer.slice(264, 284); 34 | console.log("hmac sig:", hmac_sig.toString("hex")); 35 | 36 | try { 37 | var keys = require("./keys"); 38 | const hmac_payload = full_bootcode.slice(0, full_bootcode.length - 20); 39 | 40 | // OTP values 19,20,21,22, in native byte order 41 | // this converts them to a 16 byte blob, as it would have been in ram 42 | var otp = Buffer.alloc(20,0); 43 | for (var i=0; i; 8 | __overlay__ { 9 | assigned-clock-rates = <500000000>; 10 | }; 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /smi-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void fail(const char *msg) { 12 | perror(msg); 13 | exit(1); 14 | } 15 | 16 | static void print_smi_settings(struct smi_settings *settings) { 17 | printf("width: %d\n", settings->data_width); 18 | printf("pack: %c\n", settings->pack_data ? 'Y' : 'N'); 19 | printf("read setup: %d, strobe: %d, hold: %d, pace: %d\n", settings->read_setup_time, settings->read_strobe_time, settings->read_hold_time, settings->read_pace_time); 20 | printf("write setup: %d, strobe: %d, hold: %d, pace: %d\n", settings->write_setup_time, settings->write_strobe_time, settings->write_hold_time, settings->write_pace_time); 21 | printf("dma enable: %c, passthru enable: %c\n", settings->dma_enable ? 'Y':'N', settings->dma_passthrough_enable ? 'Y':'N'); 22 | printf("dma threshold read: %d, write: %d\n", settings->dma_read_thresh, settings->dma_write_thresh); 23 | printf("dma panic threshold read: %d, write: %d\n", settings->dma_panic_read_thresh, settings->dma_panic_write_thresh); 24 | } 25 | 26 | int main(int argc, char **argv) { 27 | int count = 8; 28 | int opt; 29 | int fd = open("/dev/smi", O_RDWR); 30 | if (fd < 0) fail("cant open"); 31 | struct smi_settings settings; 32 | int ret = ioctl(fd, BCM2835_SMI_IOC_GET_SETTINGS, &settings); 33 | if (ret != 0) fail("ioctl 1"); 34 | settings.read_setup_time = 10; 35 | settings.read_strobe_time = 20; 36 | settings.read_hold_time = 40; 37 | settings.read_pace_time = 80; 38 | bool writeMode = false; 39 | 40 | while ((opt = getopt(argc, argv, "b:e:s:h:p:wE:S:H:P:")) != -1) { 41 | switch (opt) { 42 | case 'b': 43 | count = atoi(optarg); 44 | break; 45 | case 'e': 46 | settings.read_setup_time = atoi(optarg); 47 | break; 48 | case 's': 49 | settings.read_strobe_time = atoi(optarg); 50 | break; 51 | case 'h': 52 | settings.read_hold_time = atoi(optarg); 53 | break; 54 | case 'p': 55 | settings.read_pace_time = atoi(optarg); 56 | break; 57 | case 'E': 58 | settings.write_setup_time = atoi(optarg); 59 | break; 60 | case 'S': 61 | settings.write_strobe_time = atoi(optarg); 62 | break; 63 | case 'H': 64 | settings.write_hold_time = atoi(optarg); 65 | break; 66 | case 'P': 67 | settings.write_pace_time = atoi(optarg); 68 | break; 69 | case 'w': 70 | writeMode = true; 71 | break; 72 | } 73 | } 74 | 75 | ret = ioctl(fd, BCM2835_SMI_IOC_WRITE_SETTINGS, &settings); 76 | if (ret != 0) fail("ioctl 2"); 77 | print_smi_settings(&settings); 78 | uint16_t buffer[count]; 79 | if (writeMode) { 80 | for (int i=0; i 2 | // cc -x assembler-with-cpp -E uart-speed-overlay.dts -o temp -I ~/apps/rpi/linux-pi5/include 3 | // dtc temp -o uart-speed.dtbo 4 | 5 | /dts-v1/; 6 | /plugin/; 7 | 8 | /{ 9 | compatible = "brcm,bcm2712"; 10 | fragment@0 { 11 | target = <&rp1_clocks>; 12 | __overlay__ { 13 | assigned-clocks = <&rp1_clocks RP1_PLL_SYS_CORE>, 14 | <&rp1_clocks RP1_PLL_AUDIO_CORE>, 15 | // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers 16 | <&rp1_clocks RP1_PLL_SYS>, 17 | <&rp1_clocks RP1_PLL_SYS_SEC>, 18 | <&rp1_clocks RP1_PLL_AUDIO>, 19 | <&rp1_clocks RP1_PLL_AUDIO_SEC>, 20 | <&rp1_clocks RP1_CLK_SYS>, 21 | <&rp1_clocks RP1_PLL_SYS_PRI_PH>, 22 | // RP1_CLK_SLOW_SYS is used for the frequency counter (FC0) 23 | <&rp1_clocks RP1_CLK_SLOW_SYS>, 24 | <&rp1_clocks RP1_CLK_SDIO_TIMER>, 25 | <&rp1_clocks RP1_CLK_SDIO_ALT_SRC>, 26 | <&rp1_clocks RP1_CLK_ETH_TSU>, 27 | <&rp1_clocks RP1_CLK_UART>; 28 | 29 | assigned-clock-rates = <1000000000>, // RP1_PLL_SYS_CORE 30 | <1536000000>, // RP1_PLL_AUDIO_CORE 31 | <200000000>, // RP1_PLL_SYS 32 | <125000000>, // RP1_PLL_SYS_SEC 33 | <61440000>, // RP1_PLL_AUDIO 34 | <192000000>, // RP1_PLL_AUDIO_SEC 35 | <200000000>, // RP1_CLK_SYS 36 | <100000000>, // RP1_PLL_SYS_PRI_PH 37 | // Must match the XOSC frequency 38 | <50000000>, // RP1_CLK_SLOW_SYS 39 | <1000000>, // RP1_CLK_SDIO_TIMER 40 | <200000000>, // RP1_CLK_SDIO_ALT_SRC 41 | <50000000>, // RP1_CLK_ETH_TSU 42 | <100000000>; // RP1_CLK_UART 43 | }; 44 | }; 45 | }; 46 | -------------------------------------------------------------------------------- /utils/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -g `${PKG_CONFIG} --cflags libdrm` -g 2 | CXXFLAGS := ${CFLAGS} 3 | 4 | ramdumper: ramdumper.cc hexdump.cc 5 | ${CC} -o $@ $^ 6 | 7 | pv_dumper.o: pv_dumper.cc 8 | 9 | pv_dumper: pv_dumper.o map_peripherals.o 10 | ${CC} -o $@ $^ -lcommon -lbcm_host -Wall ${CFLAGS} 11 | 12 | pll-inspector.o: pll-inspector.cpp 13 | ${CC} -c -o $@ $< -fpermissive 14 | 15 | pll-inspector: pll-inspector.o map_peripherals.o 16 | ${CC} -o $@ $^ -lbcm_host -lcommon 17 | 18 | kms-test: kms-test.o drm-utils.o common.o 19 | ${CXX} -o $@ $^ -ldrm -lm 20 | 21 | transposer-test: transposer-test.o drm-utils.o common.o 22 | ${CXX} -o $@ $^ -ldrm -lm 23 | 24 | symbol_dump: symbol_dump.o 25 | ${CXX} -o $@ $^ -lcommon 26 | 27 | arm_divisor: arm_divisor.o map_peripherals.o 28 | ${CXX} -o $@ $^ -lcommon -lbcm_host 29 | 30 | tearing-test: tearing-test.o drm-utils.o common.o 31 | ${CXX} -o $@ $^ -ldrm 32 | 33 | install: ramdumper pv_dumper pll-inspector symbol_dump transposer-test arm_divisor #kms-test 34 | mkdir -pv ${out}/bin 35 | cp -vi $^ ${out}/bin/ 36 | -------------------------------------------------------------------------------- /utils/arm_divisor.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "map_peripherals.h" 5 | 6 | int main_crystal; 7 | 8 | #define dumpreg(reg) { t = reg; printf(#reg":\t 0x%x\n", t); } 9 | 10 | int main(int argc, char **argv) { 11 | uint32_t t; 12 | struct peripherals handle; 13 | open_peripherals(handle); 14 | void *mmiobase = handle.peripherals_start; 15 | if (bcm_host_is_model_pi4()) main_crystal = 54000000; 16 | else main_crystal = 19200000; 17 | 18 | int goal_freq = 1000000000; 19 | 20 | if (argc >= 2) { 21 | goal_freq = atoi(argv[1]); 22 | } 23 | double divisor = (double)goal_freq / main_crystal / 2; 24 | int ndiv = (int)divisor & 0x3ff; 25 | double frac_f = (divisor - ndiv) * (1<<20); 26 | int frac = frac_f; 27 | 28 | dumpreg(A2W_PLLB_CTRL); 29 | A2W_PLLB_CTRL = CM_PASSWORD | 0x21000 | ndiv; 30 | A2W_PLLB_FRAC = CM_PASSWORD | frac; 31 | dumpreg(A2W_PLLB_CTRL); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /utils/common.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void fatal(const char *str, int e) { 6 | fprintf(stderr, "%s: %s\n", str, strerror(e)); 7 | exit(1); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /utils/common.h: -------------------------------------------------------------------------------- 1 | void fatal(const char *str, int e); 2 | -------------------------------------------------------------------------------- /utils/default.nix: -------------------------------------------------------------------------------- 1 | { stdenv, common, libraspberrypi, libdrm, pkgconfig, lib }: 2 | 3 | stdenv.mkDerivation { 4 | name = "utils"; 5 | nativeBuildInputs = [ pkgconfig ]; 6 | buildInputs = [ 7 | common libraspberrypi libdrm 8 | "${libraspberrypi.src}/host_support" 9 | ]; 10 | src = lib.cleanSource ./.; 11 | dontStrip = true; 12 | enableParallelBuilding = true; 13 | } 14 | -------------------------------------------------------------------------------- /utils/drm-utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "drm-utils.h" 9 | #include "common.h" 10 | 11 | using namespace std; 12 | 13 | void getDefaultFramebufferSize(int fd, uint32_t crtc_id, uint32_t *width, uint32_t* heigth) { 14 | drmModeCrtcPtr crtc_info = drmModeGetCrtc(fd, crtc_id); 15 | *width = crtc_info->mode.hdisplay; 16 | *heigth = crtc_info->mode.vdisplay; 17 | 18 | drmModeFreeCrtc(crtc_info); 19 | } 20 | 21 | void showFirstFrame(int fd, uint32_t crtc_id, uint32_t conn_id, uint32_t fb_id) { 22 | drmModeCrtcPtr crtc_info = drmModeGetCrtc(fd, crtc_id); 23 | printf("showFirstFrame fb_id==%d\n", fb_id); 24 | if (drmModeSetCrtc(fd, crtc_id, fb_id, 0, 0, &conn_id, 1, &crtc_info->mode)) { 25 | fatal("drmModeSetCrtc() failed", errno); 26 | } 27 | drmModeFreeCrtc(crtc_info); 28 | } 29 | 30 | void *createFrameBuffer(int fd, uint32_t width, uint32_t heigth, uint32_t *fb_id, uint32_t *pitch) { 31 | struct drm_mode_create_dumb creq; 32 | printf("creating fb...\n"); 33 | 34 | memset(&creq, 0, sizeof(struct drm_mode_create_dumb)); 35 | creq.width = width; 36 | creq.height = heigth; 37 | creq.bpp = 24; 38 | printf("%d x %d\n", creq.width, creq.height); 39 | 40 | // create framebuffer 41 | if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) < 0) { 42 | fatal("drmIoctl DRM_IOCTL_MODE_CREATE_DUMB failed", errno); 43 | } 44 | 45 | printf("drmModeAddFB(%d, %d, %d, 24, 24, %d, %d, fb_id)\n", fd, width, heigth, creq.pitch, creq.handle); 46 | // add framebuffer to something 47 | if (drmModeAddFB(fd, width, heigth, 24, 24, creq.pitch, creq.handle, fb_id)) { 48 | fatal("drmModeAddFB failed", errno); 49 | } 50 | printf("fb_id: %d\n", *fb_id); 51 | printf("pitch: %d\n", creq.pitch); 52 | *pitch = creq.pitch; 53 | 54 | struct drm_mode_map_dumb mreq; 55 | memset(&mreq, 0, sizeof(struct drm_mode_map_dumb)); 56 | mreq.handle = creq.handle; 57 | 58 | if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq)) { 59 | fatal("drmIoctl DRM_IOCTL_MODE_MAP_DUMB failed", errno); 60 | } 61 | 62 | void *buf = (void *) mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); 63 | if (buf == MAP_FAILED) { 64 | perror("cant mmap fb"); 65 | exit(2); 66 | } 67 | 68 | return buf; 69 | } 70 | 71 | void *setupFrameBuffer(int fd, int crtc_id, uint32_t conn_id, uint32_t *width, uint32_t *heigth, uint32_t *pitch) { 72 | struct drm_mode_create_dumb creq; 73 | struct drm_mode_map_dumb mreq; 74 | uint32_t fb_id; 75 | void *buf; 76 | drmModeCrtcPtr crtc_info = drmModeGetCrtc(fd, crtc_id); 77 | 78 | memset(&creq, 0, sizeof(struct drm_mode_create_dumb)); 79 | creq.width = crtc_info->mode.hdisplay; 80 | creq.height = crtc_info->mode.vdisplay; 81 | creq.bpp = 24; 82 | printf("%d x %d\n", creq.width, creq.height); 83 | 84 | if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) < 0) { 85 | fatal("drmIoctl DRM_IOCTL_MODE_CREATE_DUMB failed", errno); 86 | } 87 | 88 | printf("drmModeAddFB(%d, %d, %d, 24, 24, %d, %d, fb_id)\n", fd, crtc_info->mode.hdisplay, crtc_info->mode.vdisplay, creq.pitch, creq.handle); 89 | if (drmModeAddFB(fd, crtc_info->mode.hdisplay, crtc_info->mode.vdisplay, 24, 24, creq.pitch, creq.handle, &fb_id)) { 90 | fatal("drmModeAddFB failed", errno); 91 | } 92 | printf("fb_id: %d\n", fb_id); 93 | printf("pitch: %d\n", creq.pitch); 94 | 95 | memset(&mreq, 0, sizeof(struct drm_mode_map_dumb)); 96 | mreq.handle = creq.handle; 97 | 98 | if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq)) { 99 | fatal("drmIoctl DRM_IOCTL_MODE_MAP_DUMB failed", errno); 100 | } 101 | 102 | buf = (void *) mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); 103 | if (buf == MAP_FAILED) { 104 | perror("cant mmap fb"); 105 | exit(2); 106 | } 107 | 108 | if (drmModeSetCrtc(fd, crtc_id, fb_id, 0, 0, &conn_id, 1, &crtc_info->mode)) { 109 | fatal("drmModeSetCrtc() failed", errno); 110 | } 111 | drmModeFreeCrtc(crtc_info); 112 | *width = creq.width; 113 | *heigth = creq.height; 114 | *pitch = creq.pitch; 115 | return buf; 116 | } 117 | 118 | string connectorTypeToStr(uint32_t type) { 119 | switch (type) { 120 | case DRM_MODE_CONNECTOR_HDMIA: // 11 121 | return "HDMIA"; 122 | case DRM_MODE_CONNECTOR_DSI: // 16 123 | return "DSI"; 124 | } 125 | return "unknown"; 126 | } 127 | 128 | void printDrmModes(int fd) { 129 | drmVersionPtr version = drmGetVersion(fd); 130 | printf("version %d.%d.%d\nname: %s\ndate: %s\ndescription: %s\n", version->version_major, version->version_minor, version->version_patchlevel, version->name, version->date, version->desc); 131 | drmFreeVersion(version); 132 | drmModeRes * modes = drmModeGetResources(fd); 133 | for (int i=0; i < modes->count_fbs; i++) { 134 | printf("FB#%d: %x\n", i, modes->fbs[i]); 135 | } 136 | for (int i=0; i < modes->count_crtcs; i++) { 137 | printf("CRTC#%d: %d\n", i, modes->crtcs[i]); 138 | drmModeCrtcPtr crtc = drmModeGetCrtc(fd, modes->crtcs[i]); 139 | printf(" buffer_id: %d\n", crtc->buffer_id); 140 | printf(" position: %dx%d\n", crtc->x, crtc->y); 141 | printf(" size: %dx%d\n", crtc->width, crtc->height); 142 | printf(" mode_valid: %d\n", crtc->mode_valid); 143 | printf(" gamma_size: %d\n", crtc->gamma_size); 144 | printf(" Mode\n clock: %d\n", crtc->mode.clock); 145 | drmModeModeInfo &mode = crtc->mode; 146 | printf(" h timings: %d %d %d %d %d\n", mode.hdisplay, mode.hsync_start, mode.hsync_end, mode.htotal, mode.hskew); 147 | printf(" v timings: %d %d %d %d %d\n", mode.vdisplay, mode.vsync_start, mode.vsync_end, mode.vtotal, mode.vscan); 148 | printf(" vrefresh: %d\n", mode.vrefresh); 149 | printf(" flags: 0x%x\n", mode.flags); 150 | printf(" type: %d\n", mode.type); 151 | printf(" name: %s\n", mode.name); 152 | drmModeFreeCrtc(crtc); 153 | } 154 | for (int i=0; i < modes->count_connectors; i++) { 155 | printf("Connector#%d: %d\n", i, modes->connectors[i]); 156 | drmModeConnectorPtr connector = drmModeGetConnector(fd, modes->connectors[i]); 157 | if (connector->connection == DRM_MODE_CONNECTED) puts(" connected!"); 158 | string typeStr = connectorTypeToStr(connector->connector_type); 159 | printf(" ID: %d\n Encoder: %d\n Type: %d %s\n type_id: %d\n physical size: %dx%d\n", connector->connector_id, connector->encoder_id, connector->connector_type, typeStr.c_str(), connector->connector_type_id, connector->mmWidth, connector->mmHeight); 160 | for (int j=0; j < connector->count_encoders; j++) { 161 | printf(" Encoder#%d:\n", j); 162 | drmModeEncoderPtr enc = drmModeGetEncoder(fd, connector->encoders[j]); 163 | printf(" ID: %d\n Type: %d\n CRTCs: 0x%x\n Clones: 0x%x\n", enc->encoder_id, enc->encoder_type, enc->possible_crtcs, enc->possible_clones); 164 | drmModeFreeEncoder(enc); 165 | } 166 | printf(" Modes: %d\n", connector->count_modes); 167 | for (int j=0; j < connector->count_modes; j++) { 168 | printf(" Mode#%d:\n", j); 169 | if (j > 1) break; 170 | drmModeModeInfo &mode = connector->modes[j]; 171 | printf(" clock: %d\n", mode.clock); 172 | printf(" h timings: %d %d %d %d %d\n", mode.hdisplay, mode.hsync_start, mode.hsync_end, mode.htotal, mode.hskew); 173 | printf(" v timings: %d %d %d %d %d\n", mode.vdisplay, mode.vsync_start, mode.vsync_end, mode.vtotal, mode.vscan); 174 | printf(" vrefresh: %d\n", mode.vrefresh); 175 | printf(" flags: 0x%x\n", mode.flags); 176 | printf(" type: %d\n", mode.type); 177 | printf(" name: %s\n", mode.name); 178 | } 179 | drmModeFreeConnector(connector); 180 | } 181 | for (int i=0; i < modes->count_encoders; i++) { 182 | printf("Encoder#%d: %d\n", i, modes->encoders[i]); 183 | } 184 | printf("min size: %dx%d\n", modes->min_width, modes->min_height); 185 | printf("max size: %dx%d\n", modes->max_width, modes->max_height); 186 | drmModeFreeResources(modes); 187 | } 188 | -------------------------------------------------------------------------------- /utils/drm-utils.h: -------------------------------------------------------------------------------- 1 | void printDrmModes(int fd); 2 | void *setupFrameBuffer(int fd, int crtc_id, uint32_t conn_id, uint32_t *width, uint32_t *heigth, uint32_t *pitch); 3 | void getDefaultFramebufferSize(int fd, uint32_t crtc_id, uint32_t *width, uint32_t* heigth); 4 | void *createFrameBuffer(int fd, uint32_t width, uint32_t heigth, uint32_t *fb_id, uint32_t *pitch); 5 | void showFirstFrame(int fd, uint32_t crtc_id, uint32_t conn_id, uint32_t fb_id); 6 | -------------------------------------------------------------------------------- /utils/hexdump.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "hexdump.h" 4 | 5 | void safe_putchar(unsigned char c) { 6 | if ((c >= ' ') && (c <= '~')) { 7 | printf("%c", c); 8 | } else { 9 | printf("."); 10 | } 11 | } 12 | 13 | // realaddr must be 16 aligned 14 | // reads from realaddr, but claims to be from reportaddr, to allow mmap usage 15 | // count must be a multiple of 16 bytes 16 | void hexdump_ram(volatile void *realaddr, uint32_t reportaddr, uint32_t count) { 17 | volatile uint32_t *buffer_start = reinterpret_cast(realaddr); 18 | for (uint32_t i = 0, fakeaddr = reportaddr; i < count; i += 16, fakeaddr += 16) { 19 | uint32_t fragment; 20 | printf("0x%08lx ", fakeaddr); 21 | for (int j=0; j<4; j++) { 22 | fragment = buffer_start[((i/4)+j)]; 23 | uint8_t a,b,c,d; 24 | a = fragment & 0xff; 25 | b = (fragment >> 8) & 0xff; 26 | c = (fragment >> 16) & 0xff; 27 | d = (fragment >> 24) & 0xff; 28 | printf("%02x %02x %02x %02x ", a,b,c,d); 29 | if (j == 1) printf(" "); 30 | } 31 | printf(" |"); 32 | for (int j=0; j<4; j++) { 33 | fragment = buffer_start[((i/4)+j)]; 34 | uint8_t a,b,c,d; 35 | a = fragment & 0xff; 36 | b = (fragment >> 8) & 0xff; 37 | c = (fragment >> 16) & 0xff; 38 | d = (fragment >> 24) & 0xff; 39 | safe_putchar(a); 40 | safe_putchar(b); 41 | safe_putchar(c); 42 | safe_putchar(d); 43 | } 44 | printf("|\n"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /utils/hexdump.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | void hexdump_ram(volatile void *realaddr, uint32_t reportaddr, uint32_t count); 8 | void safe_putchar(unsigned char c); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /utils/kms-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "drm-utils.h" 16 | 17 | using namespace std; 18 | 19 | void fatal(const char *str) { 20 | fprintf(stderr, "%s\n", str); 21 | exit(1); 22 | } 23 | 24 | /* emits uart data over DPI 25 | * config.txt contents: 26 | dpi_output_format=0x17 27 | dpi_group=2 28 | dpi_mode=87 29 | dpi_timings=50 0 0 1 0 2000 0 0 1 0 0 0 0 30 0 5000000 6 30 | dtoverlay=dpi24 31 | enable_dpi_lcd=1 32 | */ 33 | void emitUartData(void *buf, const char *msg, uint32_t width, uint32_t height, uint32_t pitch) { 34 | int len = strlen(msg); 35 | for (int y=0; y < height; y++) { 36 | for (int x=0; x < width; x++) { 37 | uint8_t *pixel = reinterpret_cast(buf + (y * pitch) + (x * 3)); 38 | if ((x/10) < len) { 39 | if (x % 10 == 0) { // start bit 40 | pixel[0] = 0; 41 | } else if (x % 10 == 9) { // stop bit 42 | pixel[0] = 255; 43 | } else { // data bits 44 | int databit = (msg[x/10] >> ((x%10)-1)) & 1; 45 | pixel[0] = databit ? 0xff : 0x00; 46 | } 47 | } else { 48 | pixel[0] = 0xff; 49 | } 50 | pixel[1] = 0; // green 51 | pixel[2] = 0; // blue 52 | } 53 | } 54 | printf("%d x %d\n", width, height); 55 | } 56 | 57 | void emitSineData(void *buf, uint32_t width, uint32_t height, uint32_t pitch) { 58 | for (int y=0; y < height; y++) { 59 | for (int x=0; x < width; x++) { 60 | uint8_t *pixel = reinterpret_cast(buf + (y * pitch) + (x * 3)); 61 | double dx = x; 62 | pixel[0] = (sin((dx / 10) * 3.14) * 0x80) + 0x80; 63 | pixel[1] = 0; 64 | pixel[2] = 0; 65 | } 66 | } 67 | } 68 | 69 | int main(int argc, char **argv) { 70 | int fd = open("/dev/dri/card1", O_RDWR); 71 | uint32_t crtc_id, connector_id; 72 | #if 0 73 | // pi4 DPI 74 | crtc_id = 52; 75 | connector_id = 54; 76 | #endif 77 | #if 1 78 | // pi4 hdmi0 79 | crtc_id = 81; 80 | connector_id = 83; 81 | #endif 82 | if (fd < 0) { 83 | perror("unable to open /dev/dri/card1"); 84 | exit(3); 85 | } 86 | uint64_t has_dumb; 87 | if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || has_dumb == 0) { 88 | fatal("drmGetCap DRM_CAP_DUMB_BUFFER failed or doesn't have dumb buffer"); 89 | } 90 | drmSetMaster(fd); 91 | printDrmModes(fd); 92 | uint32_t width,height,pitch; 93 | void *buf = setupFrameBuffer(fd, crtc_id, connector_id, &width, &height, &pitch); 94 | //emitUartData(buf,"Uart test data", width, height, pitch); 95 | emitSineData(buf, width, height, pitch); 96 | sleep(100); 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /utils/map_peripherals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "map_peripherals.h" 15 | 16 | #define PAGE_SIZE (4*1024) 17 | 18 | void *mapmem(unsigned base, unsigned size) 19 | { 20 | int mem_fd; 21 | unsigned offset = base % PAGE_SIZE; 22 | base = base - offset; 23 | size = size + offset; 24 | /* open /dev/mem */ 25 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { 26 | printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n"); 27 | exit (-1); 28 | } 29 | void *mem = mmap( 30 | 0, 31 | size, 32 | PROT_READ|PROT_WRITE, 33 | MAP_SHARED/*|MAP_FIXED*/, 34 | mem_fd, 35 | base); 36 | #ifdef DEBUG 37 | printf("base=0x%x, mem=%p\n", base, mem); 38 | #endif 39 | if (mem == MAP_FAILED) { 40 | printf("mmap error %d\n", (int)mem); 41 | exit (-1); 42 | } 43 | close(mem_fd); 44 | return (char *)mem + offset; 45 | } 46 | 47 | void unmapmem(void *addr, unsigned size) 48 | { 49 | unsigned offset = (unsigned)addr % PAGE_SIZE; 50 | addr = (char *)addr - offset; 51 | size = size + offset; 52 | int s = munmap(addr, size); 53 | if (s != 0) { 54 | printf("munmap error %d\n", s); 55 | exit (-1); 56 | } 57 | } 58 | 59 | /* 60 | * use ioctl to send mbox property message 61 | */ 62 | 63 | static int mbox_property(int file_desc, void *buf) 64 | { 65 | int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); 66 | 67 | if (ret_val < 0) { 68 | printf("ioctl_set_msg failed:%d\n", ret_val); 69 | } 70 | 71 | #ifdef DEBUG 72 | unsigned *p = buf; int i; unsigned size = *(unsigned *)buf; 73 | for (i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "map_peripherals.h" 11 | #include "hexdump.h" 12 | 13 | const char *pretty_alt_mode(uint8_t mode) { 14 | switch (mode) { 15 | case 0: return "input"; 16 | case 1: return "output"; 17 | case 2: return "alt5"; 18 | case 3: return "alt4"; 19 | case 4: return "alt0"; 20 | case 5: return "alt1"; 21 | case 6: return "alt2"; 22 | case 7: return "alt3"; 23 | default: return "error"; 24 | } 25 | } 26 | 27 | void print_gpclk(volatile uint8_t *base, uint8_t number) { 28 | volatile uint32_t *control = reinterpret_cast(base + 0x70 + (number * 8)); 29 | volatile uint32_t *divisor = reinterpret_cast(base + 0x74 + (number * 8)); 30 | uint32_t actual_control = *control; 31 | uint32_t actual_divisor = *divisor; 32 | 33 | uint8_t source = actual_control & 0xf; 34 | bool enabled = actual_control & (1 << 4); 35 | bool kill = actual_control & (1 << 5); 36 | bool busy = actual_control & (1 << 7); 37 | bool flip = actual_control & (1 << 8); 38 | uint8_t mash = (actual_control >> 9) & 3; 39 | 40 | double real_divisor = ((double)actual_divisor) / 0x1000; 41 | 42 | printf("GPCLK%d: source:%d enable:%c kill:%c busy:%c flip:%c mash:%d divisor:%f (0x%x)\n", number, source, enabled?'Y':'N', kill?'Y':'N', busy?'Y':'N', flip?'Y':'N', mash, real_divisor, actual_divisor); 43 | } 44 | 45 | int main_crystal = 54000000; // 54MHz 46 | 47 | // base: the current address of A2W_BASE (to allow mmap) 48 | // offset: given a A2W_PLL?_DIG0 register (relative to A2W_BASE) 49 | void print_pll(volatile uint8_t *base, const char *name, uint32_t offset) { 50 | volatile uint32_t *dig = reinterpret_cast(base + offset); 51 | volatile uint32_t *ana = reinterpret_cast(base + 0x10 + offset); 52 | uint32_t control = *reinterpret_cast(base + 0x100 + offset); 53 | uint32_t frac = *reinterpret_cast(base + 0x200 + offset); 54 | uint32_t kaip = *reinterpret_cast(base + 0x310 + offset); 55 | uint32_t multi = *reinterpret_cast(base + 0xf00 + offset); 56 | uint16_t ndiv = control & 0x000003ff; 57 | uint16_t pdiv = (control & 0x00007000) >> 12; 58 | bool power_down = control & 0x00010000; 59 | bool prstn = control & 0x00020000; 60 | printf("\n%4s: 0x0__\t0x1__\t0x2__\t0x3__\t0xf__\n", name); 61 | printf("0x_00 %6x\t%x\t%x\t\t%x\n", dig[0], control, frac, multi); 62 | printf("0x_04 %6x\n", dig[1]); 63 | printf("0x_08 %6x\n", dig[2]); 64 | printf("0x_0c %6x\n", dig[3]); 65 | printf("0x_10 %6x\t\t\t%x\n", ana[0], kaip); 66 | printf("0x_14 %6x\n", ana[1]); 67 | printf("0x_18 %6x\n", ana[2]); 68 | printf("0x_1c %6x\n", ana[3]); 69 | printf("nvid: %d pdiv: %d power-down: %c prstn: %c control: 0x%x\n", ndiv, pdiv, power_down?'1':'0', prstn?'1':'0', control); 70 | printf("FRAC: %d (0x%x)\n", frac, frac); 71 | float divisor = (float)ndiv + ((float)frac / (1<<20)); 72 | printf("freq: %f\n", (main_crystal / pdiv) * divisor * 2); 73 | } 74 | 75 | void print_pll_subdivider(volatile uint8_t *base, const char *name, uint32_t offset) { 76 | uint32_t control = *reinterpret_cast(base + offset); 77 | uint8_t div = control & 0xff; 78 | bool channel_enable = control & 0x00000100; 79 | bool bypass_enable = control & 0x00000200; 80 | 81 | printf("%7s: divisor:%d enable:%c bypass:%c\n", name, div, channel_enable?'1':'0', bypass_enable?'1':'0'); 82 | } 83 | 84 | void print_2nd_divider(volatile uint8_t *base, const char *name, uint32_t offset) { 85 | volatile uint32_t *regs = reinterpret_cast(base + offset); 86 | //regs[0] &= ~0x10; 87 | //regs[1] = 0x5600; 88 | printf("%sCTL: 0x%x\n%sDIV: 0x%x\n", name, regs[0], name, regs[1]); 89 | uint32_t s = regs[0]; 90 | int src = s & 0xf; 91 | int divisor = regs[1] >> 4; 92 | printf(" src: %d\n enabled: %d\n kill: %d\n busy: %d\n busyd: %d\n frac: %d\n", s & 0xf, (s >> 4)&1, (s>>5)&1, (s>>7)&1, (s>>8)&1, (s >> 9)&1); 93 | printf(" divisor: %f\n", (float)divisor / 0x100); 94 | switch (src) { 95 | case 1: 96 | printf("crystal/(0x%x>>8) == %fMHz\n", divisor, main_crystal / ( ((float)divisor) / 0x100) / 1000 / 1000 ); 97 | break; 98 | } 99 | } 100 | 101 | int main(int argc, char **argv) { 102 | struct peripherals handle; 103 | open_peripherals(handle); 104 | volatile uint8_t *addr = static_cast(handle.peripherals_start); 105 | volatile uint8_t *gpio = addr + 0x200000; 106 | volatile uint32_t *fsel = reinterpret_cast(gpio); 107 | 108 | if (bcm_host_is_model_pi4()) main_crystal = 54000000; 109 | else main_crystal = 19200000; 110 | 111 | for (int i=0; i<6; i++) { 112 | uint32_t bank_mode = fsel[i]; 113 | for (int j=0; j<10; j++) { 114 | uint8_t mode = bank_mode >> (j*3) & 7; 115 | printf("GPIO %d%d mode %s\n", i, j, pretty_alt_mode(mode)); 116 | } 117 | } 118 | 119 | volatile uint8_t *clkman_base = addr + 0x101000; 120 | print_gpclk(clkman_base, 0); 121 | print_gpclk(clkman_base, 1); 122 | print_gpclk(clkman_base, 2); 123 | 124 | volatile uint8_t *pll_base = addr + 0x102000; 125 | print_pll(pll_base, "PLLA", 0x00); 126 | print_pll_subdivider(pll_base, "A_DSI0", 0x300); 127 | print_pll_subdivider(pll_base, "A_CORE", 0x400); 128 | print_pll_subdivider(pll_base, "A_PER", 0x500); 129 | print_pll_subdivider(pll_base, "A_CCP2", 0x600); 130 | print_pll(pll_base, "PLLB", 0xe0); 131 | print_pll_subdivider(pll_base, "B_ARM", 0x3e0); 132 | print_pll_subdivider(pll_base, "B_SP0", 0x4e0); 133 | print_pll_subdivider(pll_base, "B_SP1", 0x5e0); 134 | print_pll_subdivider(pll_base, "B_SP2", 0x6e0); 135 | print_pll(pll_base, "PLLC", 0x20); 136 | print_pll_subdivider(pll_base, "C_CORE2", 0x320); 137 | print_pll_subdivider(pll_base, "C_CORE1", 0x420); 138 | print_pll_subdivider(pll_base, "C_PER", 0x520); 139 | print_pll_subdivider(pll_base, "C_CORE0", 0x620); 140 | print_pll(pll_base, "PLLD", 0x40); 141 | print_pll_subdivider(pll_base, "D_DSI0", 0x340); 142 | print_pll_subdivider(pll_base, "D_CORE", 0x440); 143 | print_pll_subdivider(pll_base, "D_PER", 0x540); 144 | print_pll_subdivider(pll_base, "D_DSI1", 0x640); 145 | if (0) { 146 | print_pll(pll_base, "PLLH", 0x60); 147 | print_pll_subdivider(pll_base, "H_AUX", 0x360); 148 | print_pll_subdivider(pll_base, "H_RCAL", 0x460); 149 | print_pll_subdivider(pll_base, "H_PIX", 0x560); 150 | } 151 | print_2nd_divider(addr, "CM_DPI", 0x101068); 152 | //hexdump_ram(((uint32_t)handle.peripherals_start) + 0x102000, 0x7e102000, 0x400); 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /utils/pv_dumper.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include "map_peripherals.h" 14 | 15 | #define BV(bit) (1 << bit) 16 | 17 | int main_crystal; 18 | 19 | struct pixel_valve { 20 | uint32_t c; 21 | uint32_t vc; 22 | uint32_t vsyncd_even; 23 | uint32_t horza; 24 | uint32_t horzb; 25 | uint32_t verta; 26 | uint32_t vertb; 27 | uint32_t verta_even; 28 | uint32_t vertb_even; 29 | uint32_t int_enable; 30 | uint32_t int_status; 31 | uint32_t h_active; 32 | }; 33 | 34 | extern "C" void dump_pv(void *mmiobase, uint32_t offset, int pvnr) { 35 | printf("\nPV%d raw dump:\n", pvnr); 36 | void *pvaddr = reinterpret_cast(mmiobase) + offset; 37 | hexdump_ram(pvaddr, 0x7e000000 + offset, 0x80); 38 | struct pixel_valve pv; 39 | volatile pixel_valve *rawpv = reinterpret_cast(pvaddr); 40 | memcpy(&pv, (void*)pvaddr, sizeof(struct pixel_valve)); 41 | int vfp, vbp, vsync, vactive; 42 | int vfp_even, vbp_even, vsync_even, vactive_even; 43 | int hfp, hbp, hsync, hactive; 44 | 45 | vfp = (pv.vertb >> 16) & 0xffff; 46 | vsync = pv.verta & 0xffff; 47 | vbp = (pv.verta >> 16) & 0xffff; 48 | vactive = pv.vertb & 0xffff; 49 | int total_scanlines = vfp + vsync + vbp + vactive; 50 | 51 | vfp_even = (pv.vertb_even >> 16) & 0xffff; 52 | vsync_even = pv.verta_even & 0xffff; 53 | vbp_even = (pv.verta_even >> 16) & 0xffff; 54 | vactive_even = pv.vertb_even & 0xffff; 55 | int total_scanlines_even = vfp_even + vsync_even + vbp_even + vactive_even; 56 | 57 | hfp = (pv.horzb >> 16) & 0xffff; 58 | hsync = pv.horza & 0xffff; 59 | hbp = (pv.horza >> 16) & 0xffff; 60 | hactive = pv.horzb & 0xffff; 61 | int scanline_length = hfp + hsync + hbp + hactive; 62 | 63 | if (0) { 64 | hbp = 30; 65 | 66 | //vsync = 1; 67 | vactive_even = vactive = 150; 68 | vfp = 262 - vsync - vbp - vactive; 69 | vfp_even = 263 - vsync_even - vbp_even - vactive_even; 70 | 71 | //hsync = 1; 72 | hactive = 720; 73 | hbp = 60; 74 | hfp = 858 - hsync - hactive - hbp; 75 | } 76 | 77 | if (0) { 78 | rawpv->horza = (hbp << 16) | hsync; 79 | rawpv->horzb = (hfp << 16) | hactive; 80 | 81 | rawpv->verta = (vbp << 16) | vsync; 82 | rawpv->vertb = (vfp << 16) | vactive; 83 | rawpv->verta_even = (vbp_even << 16) | vsync_even; 84 | rawpv->vertb_even = (vfp_even << 16) | vactive_even; 85 | } 86 | 87 | if (0) { 88 | rawpv->c = (pv.c & ~0xc) | (1 << 2); 89 | } 90 | 91 | printf("C: %x\n", pv.c); 92 | if (pv.c & BV(0)) puts(" 0 enabled"); 93 | if (pv.c & BV(1)) puts(" 1 fifo clear"); 94 | printf(" 2:3 clock mux channel: %d\n", (pv.c >> 2) & 0x3); 95 | printf(" 4:5 extra clocks per pixel: %d\n", (pv.c >> 4) & 0x3); 96 | if (pv.c & BV(12)) puts(" 12 wait for h-start"); 97 | if (pv.c & BV(13)) puts(" 13 trigger underflow"); 98 | if (pv.c & BV(14)) puts(" 14 clear at start"); 99 | printf(" 15:20 fifo full level: %d\n", (pv.c >> 15) & 0x3f); 100 | printf(" 21:23 format: %d\n", (pv.c >> 21) & 0x7); 101 | printf(" 24:31 unknown: 0x%x\n", pv.c >> 24); 102 | printf("VC: %x\n", pv.vc); 103 | if (pv.vc & BV(0)) puts(" video enable"); 104 | if (pv.vc & BV(1)) puts(" contiuous"); 105 | printf("vsyncd_even: %x\n", pv.vsyncd_even); 106 | if (0) { 107 | printf("HORZ A: %x B: %x\n", pv.horza, pv.horzb); 108 | printf(" hsync: %d\n HBP: %d\n", hsync, hbp); 109 | printf(" h_active: %d\n HFP: %d\n", hactive, hfp); 110 | printf("VERT A: %x B: %x\n", pv.verta, pv.vertb); 111 | printf(" vsync: %d\n VBP: %d\n", vsync, vbp); 112 | printf(" v_active: %d\n VFP: %d\n", vactive, vfp); 113 | } 114 | printf("VERT EVEN A: %x B: %x\n", pv.verta_even, pv.vertb_even); 115 | printf("INT enable: %x status: %x\n", pv.int_enable, pv.int_status); 116 | printf("DSI_HACT_ACT: %x\n", pv.h_active); 117 | 118 | puts( "+---------------------------------------+"); 119 | printf("| front| | | %3d/%4d |\n", vfp, vfp_even); 120 | printf("| | sync | | %3d/%4d |\n", vsync, vsync_even); 121 | printf("| | | back | %3d/%4d |\n", vbp, vbp_even); 122 | printf("| %4d | %4d | %4d | %4d x %4d/%4d |\n", hfp, hsync, hbp, hactive, vactive, vactive_even); 123 | puts( "+---------------------------------------+"); 124 | 125 | int iDivisor = 0; 126 | float fDivisior = 0; 127 | float pixel_clock; 128 | int input_clock = 0; 129 | const char *input_name = ""; 130 | switch (pvnr) { 131 | case 0: 132 | iDivisor = (CM_DPIDIV >> CM_DPIDIV_DIV_LSB) & CM_DPIDIV_DIV_SET; 133 | fDivisior = (float)iDivisor / 0x100; // divisor is a 4.8bit int 134 | int src = CM_DPICTL & 0xf; 135 | printf("CM_DPI clk src: %d\n", src); 136 | switch (src) { 137 | case 1: 138 | input_clock = main_crystal; 139 | input_name = "XOSC"; 140 | break; 141 | } 142 | if (input_clock > 0) { 143 | pixel_clock = input_clock / fDivisior; 144 | printf("pixel clock: %s / %f == %f\n", input_name, fDivisior, pixel_clock/1000/1000); 145 | printf("hsync clock(%d+%d+%d+%d==%d): %fMHz\n", hfp, hsync, hbp, hactive, scanline_length, pixel_clock / scanline_length /1000/1000); 146 | printf("vsync clock(%d+%d+%d+%d==%d): %fHz\n", vfp, vsync, vbp, vactive, total_scanlines, pixel_clock / (scanline_length * total_scanlines)); 147 | printf("total clocks per frame: %d\n", scanline_length * total_scanlines); 148 | } 149 | break; 150 | } 151 | } 152 | 153 | void print_clock(volatile void *base, uint32_t offset, const char *name) { 154 | volatile uint32_t *regs = reinterpret_cast(base + offset); 155 | //regs[0] &= ~0x10; 156 | //regs[1] = 0x5600; 157 | printf("CM_%sCTL: 0x%x\nCM_%sDIV: 0x%x\n", name, regs[0], name, regs[1]); 158 | uint32_t s = regs[0]; 159 | int src = s & 0xf; 160 | int divisor = regs[1] >> 4; 161 | printf(" src: %d\n enabled: %d\n kill: %d\n busy: %d\n busyd: %d\n frac: %d\n", s & 0xf, (s >> 4)&1, (s>>5)&1, (s>>7)&1, (s>>8)&1, (s >> 9)&1); 162 | printf(" divisor: %f\n", (float)divisor / 0x100); 163 | switch (src) { 164 | case 1: 165 | printf("crystal/(0x%x>>8) == %fMHz\n", divisor, main_crystal / ( ((float)divisor) / 0x100) / 1000 / 1000 ); 166 | break; 167 | } 168 | } 169 | 170 | void hvs_print_position0(uint32_t w) { 171 | printf("position0: 0x%x\n", w); 172 | if (bcm_host_is_model_pi4()) { 173 | printf(" x: %d y: %d\n", w & 0x3fff, (w >> 16) & 0x3fff); 174 | } else { 175 | printf(" x: %d y: %d\n", w & 0xfff, (w >> 12) & 0xfff); 176 | } 177 | } 178 | void hvs_print_control2(uint32_t w) { 179 | printf("control2: 0x%x\n", w); 180 | printf(" alpha: 0x%x\n", (w >> 4) & 0xffff); 181 | printf(" alpha mode: %d\n", (w >> 30) & 0x3); 182 | } 183 | void hvs_print_word1(uint32_t w) { 184 | printf(" word1: 0x%x\n", w); 185 | } 186 | void hvs_print_position2(uint32_t w) { 187 | printf("position2: 0x%x\n", w); 188 | printf(" width: %d height: %d\n", w & 0xffff, (w >> 16) & 0xfff); 189 | } 190 | void hvs_print_position3(uint32_t w) { 191 | printf("position3: 0x%x\n", w); 192 | } 193 | void hvs_print_pointer0(uint32_t w) { 194 | printf("pointer word: 0x%x\n", w); 195 | } 196 | void hvs_print_pointerctx0(uint32_t w) { 197 | printf("pointer context word: 0x%x\n", w); 198 | } 199 | void hvs_print_pitch0(uint32_t w) { 200 | printf("pitch word: 0x%x\n", w); 201 | } 202 | 203 | void dump_hvs(void *mmiobase, int nr, uint32_t listStart, uint32_t channel_control) { 204 | printf("SCALER_DISPLIST%d: 0x%x\nSCALER_DISPCTRL%d: 0x%x\n", nr, listStart, nr, channel_control); 205 | //if (listStart == 0) return; 206 | uint32_t offset; 207 | 208 | if (!(channel_control & 0x80000000)) return; 209 | 210 | if (bcm_host_is_model_pi4()) { 211 | offset = 0x00004000; 212 | } else { 213 | offset = 0x00002000; 214 | } 215 | volatile uint32_t *list = reinterpret_cast(mmiobase + 0x400000 + offset); 216 | for (int i=listStart; i<(listStart + 16); i++) { 217 | printf("0x%x:\ncontrol 0: 0x%x\n", i, list[i]); 218 | if (list[i] & (1<<31)) { 219 | puts("(31)END"); 220 | break; 221 | } 222 | if (list[i] & (1<<30)) { 223 | int x = i; 224 | int words = (list[i] >> 24) & 0x3f; 225 | bool unity; 226 | printf(" (3:0)format: %d\n", list[i] & 0xf); 227 | if (list[i] & (1<<4)) puts(" (4)unity"); 228 | printf(" (7:5)SCL0: %d\n", (list[i] >> 5) & 0x7); 229 | printf(" (10:8)SCL1: %d\n", (list[i] >> 8) & 0x7); 230 | if (bcm_host_is_model_pi4()) { 231 | if (list[i] & (1<<11)) puts(" (11)rgb expand"); 232 | if (list[i] & (1<<12)) puts(" (12)alpha expand"); 233 | } else { 234 | printf(" (12:11)rgb expand: %d\n", (list[i] >> 11) & 0x3); 235 | } 236 | printf(" (14:13)pixel order: %d\n", (list[i] >> 13) & 0x3); 237 | if (bcm_host_is_model_pi4()) { 238 | unity = list[i] & (1<<15); 239 | } else { 240 | unity = list[i] & (1<<4); 241 | if (list[i] & (1<<15)) puts(" (15)vflip"); 242 | if (list[i] & (1<<16)) puts(" (16)hflip"); 243 | } 244 | printf(" (18:17)key mode: %d\n", (list[i] >> 17) & 0x3); 245 | if (list[i] & (1<<19)) puts(" (19)alpha mask"); 246 | printf(" (21:20)tiling mode: %d\n", (list[i] >> 20) & 0x3); 247 | printf(" (29:24)words: %d\n", words); 248 | x++; 249 | hvs_print_position0(list[x++]); 250 | if (bcm_host_is_model_pi4()) { 251 | hvs_print_control2(list[x++]); 252 | } 253 | if (unity) { 254 | puts("unity scaling"); 255 | } else { 256 | hvs_print_word1(list[x++]); 257 | } 258 | hvs_print_position2(list[x++]); 259 | hvs_print_position3(list[x++]); 260 | hvs_print_pointer0(list[x++]); 261 | hvs_print_pointerctx0(list[x++]); 262 | hvs_print_pitch0(list[x++]); 263 | if (words > 1) { 264 | i += words - 1; 265 | } 266 | } 267 | } 268 | } 269 | 270 | void print_dpi_state(void *mmiobase) { 271 | // refer to /drivers/gpu/drm/vc4/vc4_dpi.c 272 | uint32_t c = DPI_C; 273 | printf("DPI_C: 0x%x\n", c); 274 | if (c & BV(0)) puts(" enabled"); 275 | 276 | if (c & BV(1)) puts(" output enable disabled"); 277 | if (c & BV(2)) puts(" vsync disabled"); 278 | if (c & BV(3)) puts(" hsync disabled"); 279 | 280 | if (c & BV(4)) puts(" output enable negate"); 281 | if (c & BV(5)) puts(" vsync negate"); 282 | if (c & BV(6)) puts(" hsync negate"); 283 | 284 | if (c & BV(7)) puts(" output enable invert"); 285 | if (c & BV(8)) puts(" vsync invert"); 286 | if (c & BV(9)) puts(" hsync invert"); 287 | if (c & BV(10)) puts(" pixel clk invert"); 288 | } 289 | 290 | int main(int argc, char **argv) { 291 | struct peripherals handle; 292 | open_peripherals(handle); 293 | void *mmiobase = handle.peripherals_start; 294 | if (bcm_host_is_model_pi4()) main_crystal = 54000000; 295 | else main_crystal = 19200000; 296 | //print_clock(rawaddr, 0x101068, "DPI"); 297 | puts("\nVec:"); 298 | print_clock(mmiobase, 0x1010f8, "VEC"); 299 | if (bcm_host_is_model_pi4()) { 300 | puts("pi4"); 301 | hexdump_ram(mmiobase + 0xc13000, 0x7ec13000, 0x300); 302 | } else { 303 | hexdump_ram(mmiobase + 0x806000, 0x7e806000, 0x300); 304 | } 305 | if (bcm_host_is_model_pi4()) { 306 | dump_pv(mmiobase, 0x206000, 0); 307 | dump_pv(mmiobase, 0x207000, 1); 308 | dump_pv(mmiobase, 0x20a000, 2); 309 | dump_pv(mmiobase, 0xc12000, 3); 310 | dump_pv(mmiobase, 0x216000, 4); 311 | } else { 312 | dump_pv(mmiobase, 0x206000, 0); 313 | dump_pv(mmiobase, 0x207000, 1); 314 | dump_pv(mmiobase, 0x807000, 2); 315 | } 316 | //hexdump_ram(((uint32_t)rawaddr) + 0x200000, 0x7e200000, 0x200); 317 | //hexdump_ram(mmiobase + 0x400000, 0x7e400000, 0xd0); 318 | puts(""); 319 | hexdump_ram(mmiobase + 0x404000, 0x7e404000, 0x100); 320 | dump_hvs(mmiobase, 0, SCALER_DISPLIST0, SCALER_DISPCTRL0); 321 | dump_hvs(mmiobase, 1, SCALER_DISPLIST1, SCALER_DISPCTRL1); 322 | dump_hvs(mmiobase, 2, SCALER_DISPLIST2, SCALER_DISPCTRL2); 323 | //hexdump_ram(mmiobase + 0x402000, 0x7e402000, 0x100); 324 | //hexdump_ram(mmiobase + 0x404000, 0x7e404000, 0x100); 325 | //print_dpi_state(mmiobase); 326 | } 327 | -------------------------------------------------------------------------------- /utils/ramdumper.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hexdump.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | // #include 13 | 14 | int main(int argc, char **argv) { 15 | int opt; 16 | char *device = NULL; 17 | uint32_t addr = 0; 18 | uint32_t length = 32; 19 | //bool vcmem = false; 20 | while ((opt = getopt(argc, argv, "mvgd:a:l:")) != -1) { 21 | switch (opt) { 22 | case 'm': 23 | device = "/dev/mem"; 24 | break; 25 | /* 26 | case 'v': 27 | device = "/dev/vc-mem"; 28 | vcmem = true; 29 | break; 30 | */ 31 | case 'g': 32 | device = "/dev/gpiomem"; 33 | break; 34 | case 'd': 35 | device = optarg; 36 | break; 37 | case 'a': 38 | addr = strtoll(optarg, NULL, 0); 39 | break; 40 | case 'l': 41 | length = strtol(optarg, NULL, 0); 42 | break; 43 | } 44 | } 45 | if (device == NULL) { 46 | puts("error, you must specify a device\n/dev/gpiomem is -g\n/dev/mem is -m\n/dev/vc-mem is -v\n"); 47 | return 4; 48 | } 49 | 50 | int fd = open(device, O_RDONLY); 51 | if (fd < 0) { 52 | printf("unable to open %s, ", device); 53 | perror("reason"); 54 | return 2; 55 | } 56 | /* 57 | if (vcmem) { 58 | uint32_t extra_offset = 0; 59 | int ret = ioctl(fd, VC_MEM_IOC_MEM_BASE, &extra_offset); 60 | if (ret == -1) { 61 | perror("ioctl error"); 62 | return 5; 63 | } 64 | addr += extra_offset; 65 | } 66 | */ 67 | uint32_t read_offset = addr & 0xfff; 68 | addr = (addr >> 12) << 12; 69 | volatile void *rawaddr = (uint32_t*)mmap(NULL, length + read_offset, PROT_READ, MAP_SHARED, fd, addr); 70 | if (rawaddr == MAP_FAILED) { 71 | perror("unable to mmap"); 72 | return 3; 73 | } 74 | printf("starting at 0x%x (%dMB)\n", addr+read_offset, (addr+read_offset)/1024/1024); 75 | hexdump_ram(rawaddr+read_offset, addr+read_offset, length); 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /utils/symbol_dump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define VPU_TO_VIRT(addr) ((addr - firmware_base) + rawaddr) 16 | #define REG32(addr) ((volatile uint32_t *)(VPU_TO_VIRT(addr))) 17 | 18 | int main(int argc, char **argv) { 19 | int fd = open("/dev/vc-mem", O_RDONLY); 20 | if (fd < 0) { 21 | perror("unable to open /dev/vc-mem"); 22 | return 2; 23 | } 24 | 25 | uint32_t firmware_base; 26 | int ret = ioctl(fd, VC_MEM_IOC_MEM_BASE, &firmware_base); 27 | if (ret == -1) { 28 | perror("ioctl error"); 29 | return 5; 30 | } 31 | 32 | uint32_t vcmem_size; 33 | ret = ioctl(fd, VC_MEM_IOC_MEM_SIZE, &vcmem_size); 34 | if (ret == -1) { 35 | perror("ioctl error"); 36 | return 5; 37 | } 38 | 39 | printf("firmware starts at 0x%x and ends at 0x%x\n", firmware_base, vcmem_size); 40 | volatile void *rawaddr = (uint32_t*)mmap(NULL, vcmem_size - firmware_base, PROT_READ, MAP_SHARED, fd, firmware_base); 41 | 42 | hexdump_ram(rawaddr + 0x2800, firmware_base + 0x2800, 0x30); 43 | 44 | uint32_t symbol_table = *REG32(firmware_base + 0x2800); 45 | printf("symbol table is at 0x%x\n", symbol_table); 46 | uint32_t log_start, log_end; 47 | while (true) { 48 | uint32_t name_addr = *REG32(symbol_table); 49 | char *name = VPU_TO_VIRT(name_addr); 50 | uint32_t symbol_addr = *REG32(symbol_table+4); 51 | uint32_t flags = *REG32(symbol_table+8); 52 | if (name_addr == 0) break; 53 | bool string_based = false; 54 | switch (flags) { 55 | case 0x6: 56 | case 0x9: 57 | case 0xa: 58 | case 0xc: 59 | case 0x12: 60 | case 0x31: 61 | string_based = true; 62 | break; 63 | } 64 | if (strcmp(name, "vcos_build_user") == 0) string_based = true; 65 | if (strcmp(name, "__LOG_START") == 0) log_start = *REG32(symbol_addr & 0x3fffffff) & 0x3fffffff; 66 | if (strcmp(name, "__LOG_END") == 0) log_end = *REG32(symbol_addr & 0x3fffffff) & 0x3fffffff; 67 | if (string_based) { 68 | printf("%s == %s\n", name, VPU_TO_VIRT(symbol_addr)); 69 | } else { 70 | printf("0x%x 0x%03x %s\n", symbol_addr, flags, name); 71 | } 72 | symbol_table += 12; 73 | } 74 | printf("logs span 0x%x to 0x%x\n", log_start, log_end); 75 | hexdump_ram(VPU_TO_VIRT(log_start), log_start, log_end - log_start); 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /utils/tearing-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "drm-utils.h" 12 | #include "common.h" 13 | 14 | // see also: https://github.com/liujunming/GPU_learning/blob/master/drm/kms-pageflip.c 15 | 16 | static void show_frame(int fd, uint32_t crtc_id, uint32_t fb_id) { 17 | drmModePageFlip(fd, crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, 0); 18 | } 19 | 20 | static void kms() { 21 | int fd = open("/dev/dri/card1", O_RDWR); 22 | if (fd < 0) { 23 | perror("unable to open /dev/dri/card1"); 24 | exit(3); 25 | } 26 | uint64_t has_dumb; 27 | if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || has_dumb == 0) { 28 | fatal("drmGetCap DRM_CAP_DUMB_BUFFER failed or doesn't have dumb buffer", errno); 29 | } 30 | if (drmSetMaster(fd) < 0) fatal("cant claim to be master of drm device", errno); 31 | printDrmModes(fd); 32 | 33 | int crtc, connector, encoder; 34 | 35 | crtc = 87; connector = 89; encoder = 88; 36 | uint32_t pitch0, pitch1, fb_id0, fb_id1; 37 | uint32_t width, heigth, pitch; 38 | 39 | getDefaultFramebufferSize(fd, crtc, &width, &heigth); 40 | void *buf0 = createFrameBuffer(fd, width, heigth, &fb_id0, &pitch0); 41 | void *buf1 = createFrameBuffer(fd, width, heigth, &fb_id1, &pitch1); 42 | printf("buf0: 0x%x, buf1: 0x%x\n", buf0, buf1); 43 | for (int y=0; y(buf0 + (y * pitch) + (x * 3)); 46 | pixel[0] = 255; 47 | pixel[1] = 0; 48 | pixel[2] = 0; 49 | } 50 | } 51 | for (int y=0; y(buf1 + (y * pitch) + (x * 3)); 54 | pixel[0] = 0; 55 | pixel[1] = 255; 56 | pixel[2] = 0; 57 | } 58 | } 59 | 60 | showFirstFrame(fd, crtc, connector, fb_id1); 61 | 62 | for (int i=0; i<100; i++) { 63 | show_frame(fd, crtc, fb_id0); 64 | struct drm_event_vblank event; 65 | read(fd, &event, sizeof(event)); 66 | show_frame(fd, crtc, fb_id1); 67 | read(fd, &event, sizeof(event)); 68 | } 69 | 70 | #if 0 71 | void *buf = setupFrameBuffer(fd, crtc, connector, &width, &heigth, &pitch); 72 | for (int x=0; x(buf + (y * pitch) + (x * 3)); 75 | pixel[0] = 255; 76 | pixel[1] = 255; 77 | pixel[2] = 255; 78 | } 79 | usleep(1000); 80 | } 81 | #endif 82 | } 83 | 84 | int main(int argc, char **argv) { 85 | int opt; 86 | while((opt = getopt(argc, argv, "k")) != -1) { 87 | switch (opt) { 88 | case 'k': 89 | puts("kms mode"); 90 | kms(); 91 | break; 92 | } 93 | } 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /valgrind.patch: -------------------------------------------------------------------------------- 1 | --- old/configure 2021-09-22 20:03:56.214171041 -0300 2 | +++ new/configure 2021-09-22 20:04:16.841595248 -0300 3 | @@ -5938,6 +5938,12 @@ 4 | ARCH_MAX="arm" 5 | ;; 6 | 7 | + armv8*) 8 | + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (${host_cpu})" >&5 9 | +$as_echo "ok (${host_cpu})" >&6; } 10 | + ARCH_MAX="arm" 11 | + ;; 12 | + 13 | aarch64*) 14 | { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok (${host_cpu})" >&5 15 | $as_echo "ok (${host_cpu})" >&6; } 16 | -------------------------------------------------------------------------------- /webusbboot/README.md: -------------------------------------------------------------------------------- 1 | a webusb implementation of https://github.com/raspberrypi/usbboot 2 | 3 | only tested on an rpi4 4 | 5 | click `check for pi` to discover a connected pi, and authorize the JS to control it 6 | 7 | click `push bootcode` to push the currently selected .bin file over 8 | 9 | click `refetch lk.bin` to download another lk.bin file 10 | 11 | click `fetch recovery.bin` to switch over to `recovery.bin` (usbboot must be cloned to this dir) 12 | 13 | if `auto push bootcode` is checked, it will push the last fetched .bin upon detecting a usb device 14 | 15 | also implements the fileserver used by the official firmware 16 | -------------------------------------------------------------------------------- /webusbboot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 |
10 | bootcode.bin to send: 11 | 16 |
17 | fileserver root: 18 | 24 |
25 | auto push 26 |
27 |
28 |
29 |
30 | 32 | the source! 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /webusbboot/notes.txt: -------------------------------------------------------------------------------- 1 | https://developers.google.com/web/updates/2016/03/access-usb-devices-on-the-web 2 | -------------------------------------------------------------------------------- /webusbboot/pi-usbboot.js: -------------------------------------------------------------------------------- 1 | var devices = {}; 2 | var payload; 3 | var header; 4 | const config_txt = "uart_2ndstage=1\nenable_uart=1\ndtoverlay=disable-bt"; 5 | var file_overrides = {}; 6 | file_overrides["config.txt"] = config_txt; 7 | var fileserv_root = "usbboot/recovery/"; 8 | 9 | function updateRoot() { 10 | switch (document.getElementById("rootdir").value) { 11 | case "recovery": 12 | fileserv_root = "usbboot/recovery/"; 13 | file_overrides = {}; 14 | file_overrides["config.txt"] = config_txt; 15 | break; 16 | case "msd": 17 | fileserv_root = "usbboot/msd/"; 18 | file_overrides = {}; 19 | file_overrides["config.txt"] = config_txt; 20 | break; 21 | case "linux1": 22 | fileserv_root = "linux1/"; 23 | file_overrides = {}; 24 | //file_overrides["config.txt"] = config_txt; 25 | break; 26 | case "result": 27 | fileserv_root = "result/"; 28 | file_overrides = {}; 29 | break; 30 | } 31 | } 32 | 33 | function updateBootcode() { 34 | switch (document.getElementById("bootcode").value) { 35 | case "lk": 36 | fetchFirmware('lk.bin'); 37 | break; 38 | case "recovery": 39 | fetchFirmware('usbboot/recovery/bootcode4.bin'); 40 | break; 41 | case "bootcode": 42 | fetchFirmware("usbboot/msd/bootcode.bin"); 43 | break; 44 | } 45 | } 46 | 47 | navigator.usb.getDevices() 48 | .then(devs => { 49 | console.log("pre-authorized devices", devs); 50 | for (var dev of devs) { 51 | showLog(dev.serialNumber + ": device found on startup"); 52 | handleNewDevice(dev); 53 | } 54 | }) 55 | .catch(error => { console.log(error); }); 56 | 57 | navigator.usb.onconnect = function (ev) { 58 | var dev = ev.device; 59 | showLog(dev.serialNumber + ": hotplug detected"); 60 | handleNewDevice(dev); 61 | } 62 | 63 | navigator.usb.ondisconnect = function (ev) { 64 | var dev = ev.device; 65 | if (devices[dev.serialNumber]) { 66 | devices[dev.serialNumber].dom.parentNode.removeChild(devices[dev.serialNumber].dom); 67 | } 68 | } 69 | 70 | function figureOutMode(dev) { 71 | return new Promise(function (resolve, reject) { 72 | if ((dev.vendorId == 0x0a5c) && (dev.productId == 0x2763) && (dev.productName == "BCM2708 Boot")) { 73 | return resolve("rom"); 74 | } 75 | if (dev.serialNumber == "Broadcom") { // older pi4 eeprom 76 | resolve("fileserver"); 77 | } else { 78 | dev.open().then(() => { 79 | return dev.controlTransferIn({ 80 | requestType: "standard", 81 | recipient: "device", 82 | request: 6, // get descriptor 83 | value: 1 << 8, // device descriptor 84 | index: 0, 85 | }, 256) 86 | } 87 | ).then(result => { 88 | console.log(result); 89 | var res = new Uint8Array(result.data.buffer); 90 | var mode = "unknown"; 91 | if ((res[16] == 0) || (res[16] == 3)) mode = "rom"; 92 | if (res[16] == 4) mode = "fileserver"; 93 | console.log(res); 94 | dev.close().then(() => { 95 | resolve(mode); 96 | }); 97 | }); 98 | } 99 | }); 100 | } 101 | 102 | function handleNewDevice(dev) { 103 | figureOutMode(dev).then(mode => { 104 | console.log(mode); 105 | devices[dev.serialNumber] = { dev: dev, dom: createDevice(dev, mode), mode:mode }; 106 | if (mode == "fileserver") { 107 | var cb = document.getElementById("autopush"); 108 | if (cb.checked) startFileserver(dev); 109 | } else { 110 | var cb = document.getElementById("autopush"); 111 | if (cb.checked) connectToPi(dev); 112 | } 113 | }); 114 | } 115 | 116 | function createDevice(dev, mode) { 117 | var root = document.createElement("div"); 118 | var t = document.createTextNode("pi: "+dev.serialNumber); 119 | root.appendChild(t); 120 | root.className = "device"; 121 | 122 | var b = document.createElement("input"); 123 | b.type = "button"; 124 | if (mode == "fileserver") { 125 | b.value = "start fileserver"; 126 | b.onclick = function () { 127 | startFileserver(dev); 128 | }; 129 | } else { 130 | b.value = "push bootcode"; 131 | b.onclick = function () { 132 | connectToPi(dev); 133 | }; 134 | } 135 | 136 | root.appendChild(b); 137 | 138 | document.getElementById("devices").appendChild(root); 139 | return root; 140 | } 141 | function flagDomFinished(dev) { 142 | var r = devices[dev.serialNumber].dom; 143 | var b = r.getElementsByTagName("input")[0]; 144 | r.removeChild(b); 145 | r.appendChild(document.createElement("br")); 146 | r.appendChild(document.createTextNode("booting...")); 147 | } 148 | 149 | function showLog(msg) { 150 | var node = document.createElement("div"); 151 | node.appendChild(document.createTextNode(msg)); 152 | document.getElementById("console").appendChild(node); 153 | try { 154 | node.scrollIntoView(); 155 | } catch (e) { 156 | } 157 | return node; 158 | } 159 | 160 | function requestAccess() { 161 | var filters = [ 162 | { vendorId: 0x0a5c, productId: 0x2763 }, // pi0 163 | { vendorId: 0x0a5c, productId: 0x2711 }, // pi4 ROM, bootcode.bin, and recovery.bin 164 | { vendorId: 0x0a5c, productId: 0x2764 } // pi4 start4.elf 165 | ]; 166 | navigator.usb.requestDevice({ filters: filters }) 167 | .then(dev => { 168 | showLog(dev.serialNumber + ": access granted"); 169 | handleNewDevice(dev); 170 | }) 171 | .catch(error => { console.log(error); }); 172 | } 173 | 174 | function ep_read(dev, size) { 175 | return dev.controlTransferIn({ 176 | requestType: 'vendor', 177 | recipient: 'device', 178 | request: 0x0, 179 | value: size & 0xffff, 180 | index: (size >> 16) & 0xffff 181 | }, size) 182 | .then(result => { 183 | if (result.status != "ok") throw new Error("ep_read failed"); 184 | return result; 185 | }) 186 | } 187 | 188 | function ep_write(dev, buf, size, progNode) { 189 | var maxSize = 1024 * 1024 * 4; // 4mb chunk size 190 | var veryStart = Date.now(); 191 | function send_chunk(start, sizeRemain, rate) { 192 | console.log("sending chunk", start, sizeRemain); 193 | if (sizeRemain > 0) { 194 | if (progNode) { 195 | progNode.innerText = " " + Math.floor((start / size) * 100) + "%" + rate; 196 | } 197 | var startTime = Date.now(); 198 | return dev.transferOut(1, buf.slice(start, start + maxSize)) 199 | .then(() => { 200 | var endTime = Date.now(); 201 | var rate = maxSize / ((endTime-startTime)/1000); 202 | var remain = size - (start + maxSize); 203 | return send_chunk(start + maxSize, sizeRemain - maxSize, " " + Math.floor(rate/1024) + "KB/sec " + (Math.floor(remain / rate) + " sec remaining")); 204 | }); 205 | } else { 206 | if (progNode) { 207 | var veryEnd = Date.now(); 208 | progNode.innerText = " 100% " + Math.floor(size / ((veryEnd-veryStart)/1000) / 1024) + "KB/sec"; 209 | } 210 | } 211 | } 212 | return control_out(dev, size) 213 | .then(() => { 214 | console.log("control out for size"); 215 | return send_chunk(0, size); 216 | }) 217 | } 218 | 219 | function checkFileserverRequest(dev, result) { 220 | var bit32view = new Uint32Array(result.data.buffer); 221 | var control = bit32view[0]; 222 | var namebuf = result.data.buffer.slice(4); 223 | var namelen = (new Uint8Array(namebuf)).indexOf(0); 224 | var filename = String.fromCharCode.apply(null, new Uint8Array(namebuf.slice(0,namelen))); 225 | switch (control) { 226 | case 0: // get file size 227 | switch (filename) { 228 | default: 229 | if (file_overrides[filename]) { 230 | return control_out(dev, file_overrides[filename].length) 231 | .then(() => { return fileserverMain(dev); }); 232 | } else { 233 | return asyncFetch(fileserv_root + filename) 234 | .then(reply_body => { 235 | if (reply_body !== null) { 236 | //showLog(dev.serialNumber+": sending size of "+filename+" as "+reply_body.byteLength); 237 | return control_out(dev, reply_body.byteLength); 238 | } else { 239 | showLog(dev.serialNumber+": file not found: "+filename); 240 | return ep_write(dev, null, 0); 241 | } 242 | }); 243 | } 244 | break; 245 | } 246 | break; 247 | case 1: // read file 248 | var node = showLog(dev.serialNumber + ": file read " + filename); 249 | var progNode = document.createElement("span"); 250 | node.appendChild(progNode); 251 | switch (filename) { 252 | default: 253 | if (file_overrides[filename]) { 254 | var rawbuf = str2ab(file_overrides[filename]); 255 | return ep_write(dev, rawbuf, rawbuf.byteLength); 256 | } else { 257 | return asyncFetch(fileserv_root + filename) 258 | .then(reply_body => { 259 | return ep_write(dev, reply_body, reply_body.byteLength, progNode); 260 | }); 261 | } 262 | } 263 | break; 264 | case 2: // done 265 | console.log("DONE!"); 266 | dev.close(); 267 | document.getElementById("sound_done").play(); 268 | break; 269 | } 270 | } 271 | 272 | function str2ab(str) { 273 | var buf = new ArrayBuffer(str.length); // 1 byte for each char 274 | var bufView = new Uint8Array(buf); 275 | for (var i=0, strLen=str.length; i < strLen; i++) { 276 | bufView[i] = str.charCodeAt(i); 277 | } 278 | return buf; 279 | } 280 | 281 | function control_out(dev, size) { 282 | var obj = { 283 | requestType: 'vendor', 284 | recipient: 'device', 285 | request: 0x0, 286 | value: size & 0xffff, 287 | index: (size >> 16) & 0xffff 288 | }; 289 | console.log("raw out", obj); 290 | return dev.controlTransferOut(obj) 291 | .then(result => { 292 | if (result.status != "ok") throw new Error("control out failed"); 293 | }); 294 | } 295 | 296 | function startFileserver(dev) { 297 | dev 298 | .open() 299 | .then(() => dev.selectConfiguration(1)) 300 | .then(() => { 301 | console.log("claiming for fileserver"); 302 | return dev.claimInterface(0) 303 | }) 304 | .then(() => { return fileserverMain(dev); }) 305 | .catch(error => { 306 | console.log(error); 307 | }); 308 | } 309 | function fileserverMain(dev) { 310 | return ep_read(dev, 260) 311 | .then(result => { return checkFileserverRequest(dev, result); }) 312 | .then(() => { return fileserverMain(dev); }); 313 | } 314 | 315 | function connectToPi(dev) { 316 | var header_length = header.byteLength; 317 | var length = payload.byteLength; 318 | var position = 0; 319 | function checkChunk(result) { 320 | console.log("range "+position+"-"+(position+result.bytesWritten)+" successfully sent"); 321 | position += result.bytesWritten; 322 | if (position == payload.byteLength) { 323 | console.log("done"); 324 | dev.controlTransferIn({ 325 | requestType: 'vendor', 326 | recipient: 'device', 327 | request: 0x0, 328 | value: 4, 329 | index: 0, 330 | }, 4) 331 | .then(result => { 332 | console.log(result); 333 | var res2 = new Uint32Array(result.data); 334 | console.log(res2) 335 | dev.close(); 336 | flagDomFinished(dev); 337 | }) 338 | return; 339 | } 340 | return dev.transferOut(1, payload.slice(position, position+16384)) 341 | .then(result => { 342 | return checkChunk(result); 343 | }) 344 | .catch(error => { console.log(error); }); 345 | } 346 | dev.open() 347 | .then(() => { 348 | console.log("open worked, now setting configuration"); 349 | return dev.selectConfiguration(1); 350 | }) 351 | .then(() => { 352 | console.log("claiming"); 353 | return dev.claimInterface(0) 354 | }) 355 | .then(() => { 356 | console.log("control 1"); 357 | return dev.controlTransferOut({ 358 | requestType: 'vendor', 359 | recipient: 'device', 360 | request: 0x0, 361 | value: header_length & 0xffff, 362 | index: (header_length >> 16) & 0xffff 363 | }) 364 | }) // Ready to receive data 365 | .then(() => { 366 | console.log("header transfer"); 367 | return dev.transferOut(1, header) 368 | }) 369 | .then(headerResult => { 370 | console.log(headerResult); 371 | console.log("control 2"); 372 | var obj = { 373 | requestType: 'vendor', 374 | recipient: 'device', 375 | request: 0x0, 376 | value: length & 0xffff, 377 | index: (length >> 16) & 0xffff 378 | }; 379 | console.log(obj); 380 | return dev.controlTransferOut(obj); 381 | }) // Ready to receive data 382 | .then(control2Result => { 383 | console.log(control2Result); 384 | console.log("payload transfering...") 385 | return dev.transferOut(1, payload.slice(0, 16384)) 386 | }) 387 | .then(result => { 388 | return checkChunk(result); 389 | }) 390 | //.catch(error => { console.log(error); }); 391 | } 392 | 393 | function fetchFirmware(relativepath) { 394 | asyncFetch(relativepath) 395 | .then(arrayBuffer => { 396 | console.log("fetched "+relativepath); 397 | payload = arrayBuffer; 398 | header = new ArrayBuffer(24); 399 | new DataView(header).setInt32(0, arrayBuffer.byteLength, true); 400 | }); 401 | } 402 | 403 | function asyncFetch(path) { 404 | return new Promise(function (resolve, reject) { 405 | var oReq = new XMLHttpRequest(); 406 | oReq.open("GET", path, true); 407 | oReq.responseType = "arraybuffer"; 408 | oReq.onload = function (oEvent) { 409 | if (oReq.status == 200) { 410 | var arrayBuffer = oReq.response; // Note: not oReq.responseText 411 | if (arrayBuffer) { 412 | //console.log("fetched "+arrayBuffer.byteLength+" bytes of "+path); 413 | resolve(arrayBuffer); 414 | } 415 | } else { 416 | resolve(null); 417 | } 418 | }; 419 | oReq.send(null); 420 | }); 421 | } 422 | 423 | setTimeout(function () { 424 | updateBootcode(); 425 | updateRoot(); 426 | }, 10); 427 | -------------------------------------------------------------------------------- /webusbboot/ping.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/librerpi/rpi-tools/bb38f0bd6150b7bb9bd2a01b234593bcf454f73a/webusbboot/ping.mp3 -------------------------------------------------------------------------------- /webusbboot/ping.txt: -------------------------------------------------------------------------------- 1 | https://freesound.org/people/Snapper4298/sounds/179198/ 2 | -------------------------------------------------------------------------------- /webusbboot/style.css: -------------------------------------------------------------------------------- 1 | #console { 2 | height: 50%; 3 | overflow-y: scroll; 4 | border: 1px solid green; 5 | } 6 | 7 | .device { 8 | border: 1px solid red; 9 | display: block; 10 | } 11 | --------------------------------------------------------------------------------