From: Michael Buesch Date: Sat, 10 Nov 2007 22:12:26 +0000 (+0100) Subject: Initial commit. X-Git-Tag: b43-fwcutter-013~89 X-Git-Url: https://jxself.org/git/?a=commitdiff_plain;h=b9e19faa25ad9e1b24f08dc77267760df1b98504;p=b43-tools.git Initial commit. Signed-off-by: Michael Buesch --- b9e19faa25ad9e1b24f08dc77267760df1b98504 diff --git a/assembler/COPYING b/assembler/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/assembler/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/assembler/Makefile b/assembler/Makefile new file mode 100644 index 0000000..b9c3e66 --- /dev/null +++ b/assembler/Makefile @@ -0,0 +1,47 @@ +CC = gcc +LEX = flex +YACC = bison +PREFIX = /usr/local +CFLAGS = -std=c99 -O2 -fomit-frame-pointer -Wall -D_BSD_SOURCE -D_GNU_SOURCE +LDFLAGS = -lfl + +BINARY = bcm43xx-asm.bin +OBJECTS = parser.o scanner.o main.o initvals.o util.o args.o + +# YACC related CFLAGS +CFLAGS += -DYYSTYPE="void *" -DYYERROR_VERBOSE -DYYDEBUG -Wno-unused + +all: $(BINARY) + +scanner.c: scanner.l parser.c main.h + $(LEX) -o scanner.c --header-file=scanner.h scanner.l + +scanner.o: scanner.c + $(CC) $(CFLAGS) -c -o scanner.o scanner.c + +parser.c: parser.y main.h util.h + $(YACC) --defines -o parser.c parser.y + +parser.o: parser.c + $(CC) $(CFLAGS) -c -o parser.o parser.c + +main.o: parser.c main.h list.h util.h args.h initvals.h + +initvals.o: initvals.h main.h list.h util.h args.h + +util.o: util.h + +args.o: args.h main.h util.h + +$(BINARY): $(OBJECTS) + $(CC) $(CFLAGS) -o $(BINARY) $(OBJECTS) $(LDFLAGS) + +install: all + -install -o 0 -g 0 -m 755 $(BINARY) $(PREFIX)/bin/ + -cp bcm43xx-asm bcm43xx-asm.inst + -sed -i -e 's/installed=0/installed=1/' bcm43xx-asm.inst + -install -o 0 -g 0 -m 755 bcm43xx-asm.inst $(PREFIX)/bin/bcm43xx-asm + -rm -f bcm43xx-asm.inst + +clean: + -rm -f *~ *.o *.orig *.rej $(BINARY) scanner.c scanner.h parser.c parser.h diff --git a/assembler/README b/assembler/README new file mode 100644 index 0000000..6658803 --- /dev/null +++ b/assembler/README @@ -0,0 +1,83 @@ +=============================================================================== +== Instruction Set Summary for the core revision >= 5 microcode == +=============================================================================== + + +=============================================================================== +Mnemonics | Operands | Description | Operation +=============================================================================== + +Arithmetic and logic instructions: +add | A,B,rD | Add | rD=A+B +add. | A,B,rD | Add, set Carry | rD=A+B SaveCarry +addc | A,B,rD | Add with Carry | rD=A+B+Carry +addc. | A,B,rD | Add with Carry, set Carry | rD=A+B+C SaveCarry +sub | A,B,rD | Subtract | rD=A-B +sub. | A,B,rD | Sub, set Carry | rD=A-B SaveCarry +subc | A,B,rD | Sub with Carry | rD=A-B-Carry +subc. | A,B,rD | Sub with Carry, set Carry | rD=A-B-C SaveCarry + +Branch instructions: +jmp | l | Unconditional jump | PC=l +jand | A,B,l | Jump if binary AND | if(A&B) PC=l +jnand | A,B,l | Jump if not binary AND | if(!(A&B)) PC=l +js | A,B,l | Jump if all bits set | if((A&B)==A) PC=l +jns | A,B,l | Jump if not all bits set | if((A&B)!=A) PC=l +je | A,B,l | Jump if equal | if(A==B) PC=l +jne | A,B,l | Jump if not equal | if(A!=B) PC=l +jls | A,B,l | Jump if less (signed) | if(A=B) PC=l +jgs | A,B,l | Jump if greater (signed) | if(A>B) PC=l +jles | A,B,l | Jump if less or equal (signed) | if(A<=B) PC=l +jl | A,B,l | Jump if less | if(A=B) PC=l +jg | A,B,l | Jump if greater | if(A>B) PC=l +jle | A,B,l | Jump if less or equal | if(A<=B) PC=l +call | lrX,l | Store PC, call function | lrX=PC; PC=l +ret | lrX,lrY | Store PC, ret from func | lrX=PC; PC=lrY +jzx | M,S,A,B,l | Jump if zero after shift + mask | +jnzx | M,S,A,B,l | Jump if nonzero after shift+msk | +jext | E,A,B,l | Jump if External Condition true | if(E) PC=l +jnext | E,A,B,l | Jump if External Condition false| if(!E) PC=l + +Data transfer instructions: +mov | A,rD | Copy data | rD=A +tkiph | A,rD | TKIP S-Box lookup high | rD=SBOX[hi8(A)] +tkiphs | A,rD | TKIP S-Box lkup hi swap'd | rD=byteswap(tkiph) +tkipl | A,rD | TKIP S-Box lookup low | rD=SBOX[lo8(A)] +tkipls | A,rD | TKIP S-Box lkup lo swap'd | rD=byteswap(tkipl) + +Bitwise instructions: +sra | A,B,rD | Arithmetic rightshift | rD=A>>B fillup sign +or | A,B,rD | Bitwise OR | rD=A|B +and | A,B,rD | Bitwise AND | rD=A&B +xor | A,B,rD | Bitwise XOR | rD=A^B +sr | A,B,rD | Rightshift | rD=A>>B +sl | A,B,rD | Leftshift | rD=A< + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "args.h" +#include "main.h" +#include "util.h" + +#include +#include +#include +#include +#include + + +int _debug; + +#define ARG_MATCH 0 +#define ARG_NOMATCH 1 +#define ARG_ERROR -1 + +static int do_cmp_arg(char **argv, int *pos, + const char *template, + int allow_merged, + char **param) +{ + char *arg; + char *next_arg; + size_t arg_len, template_len; + + arg = argv[*pos]; + next_arg = argv[*pos + 1]; + arg_len = strlen(arg); + template_len = strlen(template); + + if (param) { + /* Maybe we have a merged parameter here. + * A merged parameter is "-pfoobar" for example. + */ + if (allow_merged && arg_len > template_len) { + if (memcmp(arg, template, template_len) == 0) { + *param = arg + template_len; + return ARG_MATCH; + } + return ARG_NOMATCH; + } else if (arg_len != template_len) + return ARG_NOMATCH; + *param = next_arg; + } + if (strcmp(arg, template) == 0) { + if (param) { + /* Skip the parameter on the next iteration. */ + (*pos)++; + if (*param == 0) { + fprintf(stderr, "%s needs a parameter\n", arg); + return ARG_ERROR; + } + } + return ARG_MATCH; + } + + return ARG_NOMATCH; +} + +/* Simple and lean command line argument parsing. */ +static int cmp_arg(char **argv, int *pos, + const char *long_template, + const char *short_template, + char **param) +{ + int err; + + if (long_template) { + err = do_cmp_arg(argv, pos, long_template, 0, param); + if (err == ARG_MATCH || err == ARG_ERROR) + return err; + } + err = ARG_NOMATCH; + if (short_template) + err = do_cmp_arg(argv, pos, short_template, 1, param); + return err; +} + +static void usage(int argc, char **argv) +{ + fprintf(stderr, "Usage: %s INPUT_FILE OUTPUT_FILE [OPTIONS]\n", argv[0]); + fprintf(stderr, " -h|--help Print this help\n"); + fprintf(stderr, " -d|--debug Print verbose debugging info\n"); + fprintf(stderr, " Repeat for more verbose debugging\n"); +} + +int parse_args(int argc, char **argv) +{ + int i; + int res; + + if (argc < 3) + goto out_usage; + infile_name = argv[1]; + outfile_name = argv[2]; + + for (i = 3; i < argc; i++) { + if ((res = cmp_arg(argv, &i, "--help", "-h", 0)) == ARG_MATCH) { + goto out_usage; + } else if ((res = cmp_arg(argv, &i, "--debug", "-d", 0)) == ARG_MATCH) { + _debug++; + } else { + fprintf(stderr, "Unrecognized argument: %s\n", argv[i]); + goto out_usage; + } + } + return 0; +out_usage: + usage(argc, argv); + return -1; +} + +int open_input_file(void) +{ + int fd; + int err; + + if (strcmp(infile_name, "-") == 0) { + /* infile == stdin */ + fd = STDIN_FILENO; + } else { + fd = open(infile_name, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Could not open INPUT_FILE %s\n", + infile_name); + return -1; + } + err = dup2(fd, STDIN_FILENO); + if (err) { + fprintf(stderr, "Could not dup INPUT_FILE %s " + "to STDIN\n", infile_name); + close(fd); + return -1; + } + } + infile.fd = fd; + + return 0; +} + +void close_input_file(void) +{ + if (strcmp(infile_name, "-") != 0) + close(infile.fd); +} diff --git a/assembler/args.h b/assembler/args.h new file mode 100644 index 0000000..8bebeac --- /dev/null +++ b/assembler/args.h @@ -0,0 +1,14 @@ +#ifndef BCM43xx_ASM_ARGS_H_ +#define BCM43xx_ASM_ARGS_H_ + +int parse_args(int argc, char **argv); +int open_input_file(void); +void close_input_file(void); + +extern int _debug; + +#define IS_DEBUG (_debug > 0) +#define IS_VERBOSE_DEBUG (_debug > 1) +#define IS_INSANE_DEBUG (_debug > 2) + +#endif /* BCM43xx_ASM_ARGS_H_ */ diff --git a/assembler/bcm43xx-asm b/assembler/bcm43xx-asm new file mode 100755 index 0000000..de730c7 --- /dev/null +++ b/assembler/bcm43xx-asm @@ -0,0 +1,27 @@ +#!/bin/sh + +installed=0 + +if [ -z "$BCM43xx_ASM" ]; then + if [ $installed -eq 0 ] && [ -x "./bcm43xx-asm.bin" ]; then + BCM43xx_ASM="./bcm43xx-asm.bin" + else + BCM43xx_ASM="bcm43xx-asm.bin" + fi +fi + +if [ -z "$CPP" ]; then + CPP="cpp" +fi + +if [ $# -lt 2 ]; then + $BCM43xx_ASM --help + exit 1 +fi + +infile="$1" +shift +outfile="$1" +shift + +cat "$infile" | $CPP -traditional-cpp | $BCM43xx_ASM "-" "$outfile" $@ diff --git a/assembler/initvals.c b/assembler/initvals.c new file mode 100644 index 0000000..eb2634d --- /dev/null +++ b/assembler/initvals.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2007 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "initvals.h" +#include "list.h" +#include "util.h" +#include "args.h" + +#include +#include +#include + + +struct initval { + unsigned int offset; + unsigned int size; +#define SIZE_16BIT 2 +#define SIZE_32BIT 4 + unsigned int value; + + struct list_head list; +}; + +/* The IV in the binary file */ +struct initval_raw { + be16_t offset_size; + union { + be16_t d16; + be32_t d32; + } data __attribute__((__packed__)); +} __attribute__((__packed__)); + +#define FW_IV_OFFSET_MASK 0x7FFF +#define FW_IV_32BIT 0x8000 + + +struct ivals_context { + /* Pointer to the parsed section structure */ + const struct initvals_sect *sect; + /* List of struct initval */ + struct list_head ivals; + /* Number of initvals. */ + unsigned int ivals_count; +}; + +#define _msg_helper(type, ctx, msg, x...) do { \ + fprintf(stderr, "InitVals " type); \ + fprintf(stderr, " (Section \"%s\")", ctx->sect->name); \ + fprintf(stderr, ":\n " msg "\n" ,##x); \ + } while (0) + +#define iv_error(ctx, msg, x...) do { \ + _msg_helper("ERROR", ctx, msg ,##x); \ + exit(1); \ + } while (0) + +#define iv_warn(ctx, msg, x...) \ + _msg_helper("warning", ctx, msg ,##x) + +#define iv_info(ctx, msg, x...) \ + _msg_helper("info", ctx, msg ,##x) + + +static void assemble_write_mmio(struct ivals_context *ctx, + unsigned int offset, + unsigned int size, + unsigned int value) +{ + struct initval *iv; + + iv = xmalloc(sizeof(struct initval)); + iv->offset = offset; + iv->size = size; + iv->value = value; + INIT_LIST_HEAD(&iv->list); + list_add_tail(&iv->list, &ctx->ivals); + ctx->ivals_count++; +} + +static void assemble_write_phy(struct ivals_context *ctx, + unsigned int offset, + unsigned int value) +{ + assemble_write_mmio(ctx, 0x3FC, SIZE_16BIT, offset); + assemble_write_mmio(ctx, 0x3FE, SIZE_16BIT, value); +} + +static void assemble_write_radio(struct ivals_context *ctx, + unsigned int offset, + unsigned int value) +{ + assemble_write_mmio(ctx, 0x3F6, SIZE_16BIT, offset); + assemble_write_mmio(ctx, 0x3FA, SIZE_16BIT, value); +} + +static void shm_control_word(struct ivals_context *ctx, + unsigned int routing, + unsigned int offset) +{ + unsigned int control; + + control = (routing & 0xFFFF); + control <<= 16; + control |= (offset & 0xFFFF); + assemble_write_mmio(ctx, 0x160, SIZE_32BIT, control); +} + +static void shm_write32(struct ivals_context *ctx, + unsigned int routing, + unsigned int offset, + unsigned int value) +{ + if ((routing & 0xFF) == 0x01) { + /* Is SHM Shared-memory */ +//TODO assert((offset & 0x0001) == 0); + if (offset & 0x0003) { + /* Unaligned access */ + shm_control_word(ctx, routing, offset >> 2); + assemble_write_mmio(ctx, 0x166, SIZE_16BIT, + (value >> 16) & 0xFFFF); + shm_control_word(ctx, routing, (offset >> 2) + 1); + assemble_write_mmio(ctx, 0x164, SIZE_16BIT, + (value & 0xFFFF)); + return; + } + offset >>= 2; + } + shm_control_word(ctx, routing, offset); + assemble_write_mmio(ctx, 0x164, SIZE_32BIT, value); +} + +static void shm_write16(struct ivals_context *ctx, + unsigned int routing, + unsigned int offset, + unsigned int value) +{ + if ((routing & 0xFF) == 0x01) { + /* Is SHM Shared-memory */ +//TODO assert((offset & 0x0001) == 0); + if (offset & 0x0003) { + /* Unaligned access */ + shm_control_word(ctx, routing, offset >> 2); + assemble_write_mmio(ctx, 0x166, SIZE_16BIT, + value); + return; + } + offset >>= 2; + } + shm_control_word(ctx, routing, offset); + assemble_write_mmio(ctx, 0x164, SIZE_16BIT, value); +} + +static void assemble_write_shm(struct ivals_context *ctx, + unsigned int routing, + unsigned int offset, + unsigned int value, + unsigned int size) +{ + switch (routing & 0xFF) { + case 0: case 1: case 2: case 3: case 4: + break; + default: + //TODO error + break; + } + //TODO check offset + + //TODO check value + switch (size) { + case SIZE_16BIT: + shm_write16(ctx, routing, offset, value); + break; + case SIZE_32BIT: + shm_write32(ctx, routing, offset, value); + break; + default: + fprintf(stderr, "Internal assembler BUG. SHMwrite invalid size\n"); + exit(1); + } +} + +static void assemble_ival_section(struct ivals_context *ctx, + const struct initvals_sect *sect) +{ + struct initval_op *op; + + ctx->sect = sect; + if (list_empty(§->ops)) { + //TODO warning + return; + } + list_for_each_entry(op, §->ops, list) { + switch (op->type) { + case IVAL_W_MMIO16: + assemble_write_mmio(ctx, op->args[1], + SIZE_16BIT, + op->args[0]); + break; + case IVAL_W_MMIO32: + assemble_write_mmio(ctx, op->args[1], + SIZE_32BIT, + op->args[0]); + break; + case IVAL_W_PHY: + assemble_write_phy(ctx, op->args[1], + op->args[0]); + break; + case IVAL_W_RADIO: + assemble_write_radio(ctx, op->args[1], + op->args[0]); + break; + case IVAL_W_SHM16: + assemble_write_shm(ctx, op->args[1], + op->args[2], + op->args[0], + SIZE_16BIT); + break; + case IVAL_W_SHM32: + assemble_write_shm(ctx, op->args[1], + op->args[2], + op->args[0], + SIZE_32BIT); + break; + } + } +} + +static unsigned int initval_to_raw(struct ivals_context *ctx, + struct initval_raw *raw, + const struct initval *iv) +{ + unsigned int size; + + memset(raw, 0, sizeof(*raw)); + if (iv->offset & ~FW_IV_OFFSET_MASK) { + iv_error(ctx, "Initval offset 0x%04X too big. " + "Offset must be <= 0x%04X", + iv->offset, FW_IV_OFFSET_MASK); + } + raw->offset_size = cpu_to_be16(iv->offset); + + switch (iv->size) { + case SIZE_16BIT: + raw->data.d16 = cpu_to_be16(iv->value); + size = sizeof(be16_t) + sizeof(be16_t); + break; + case SIZE_32BIT: + raw->data.d32 = cpu_to_be32(iv->value); + raw->offset_size |= cpu_to_be16(FW_IV_32BIT); + size = sizeof(be16_t) + sizeof(be32_t); + break; + default: + iv_error(ctx, "Internal error. initval_to_raw invalid size."); + break; + } + + return size; +} + +static void emit_ival_section(struct ivals_context *ctx) +{ + FILE *fd; + char *fn; + size_t fn_len; + struct initval *iv; + struct initval_raw raw; + struct fw_header hdr; + unsigned int size; + + memset(&hdr, 0, sizeof(hdr)); + hdr.type = FW_TYPE_IV; + hdr.ver = FW_HDR_VER; + hdr.size = cpu_to_be32(ctx->ivals_count); + + fn_len = strlen(outfile_name) + 512; + fn = xmalloc(fn_len); + snprintf(fn, fn_len, "%s.%s.initval", outfile_name, ctx->sect->name); + fd = fopen(fn, "w+"); + if (!fd) { + fprintf(stderr, "Could not open initval output file \"%s\"\n", fn); + free(fn); + exit(1); + } + + if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) { + fprintf(stderr, "Could not write initvals outfile\n"); + exit(1); + } + + if (IS_VERBOSE_DEBUG) + fprintf(stderr, "\nInitvals \"%s\":\n", ctx->sect->name); + list_for_each_entry(iv, &ctx->ivals, list) { + if (IS_VERBOSE_DEBUG) { + fprintf(stderr, "%04X %u %08X\n", + iv->offset, + iv->size, + iv->value); + } + size = initval_to_raw(ctx, &raw, iv); + if (fwrite(&raw, size, 1, fd) != 1) { + fprintf(stderr, "Could not write initvals outfile\n"); + exit(1); + } + } + fclose(fd); + free(fn); +} + +void assemble_initvals(void) +{ + struct ivals_context ctx; + struct initvals_sect *sect; + + list_for_each_entry(sect, &infile.ivals, list) { + memset(&ctx, 0, sizeof(ctx)); + INIT_LIST_HEAD(&ctx.ivals); + + assemble_ival_section(&ctx, sect); + emit_ival_section(&ctx); + } +} diff --git a/assembler/initvals.h b/assembler/initvals.h new file mode 100644 index 0000000..0afecf2 --- /dev/null +++ b/assembler/initvals.h @@ -0,0 +1,35 @@ +#ifndef BCM43xx_ASM_INITVALS_H_ +#define BCM43xx_ASM_INITVALS_H_ + +#include "main.h" + + +struct initval_op { + enum { + IVAL_W_MMIO16, + IVAL_W_MMIO32, + IVAL_W_PHY, + IVAL_W_RADIO, + IVAL_W_SHM16, + IVAL_W_SHM32, + } type; + unsigned int args[3]; + + struct lineinfo info; + + struct list_head list; +}; + +struct initvals_sect { + /* The name string for this initvals section */ + const char *name; + /* List of initval operations */ + struct list_head ops; + + struct list_head list; +}; + + +extern void assemble_initvals(void); + +#endif /* BCM43xx_ASM_INITVALS_H_ */ diff --git a/assembler/list.h b/assembler/list.h new file mode 100644 index 0000000..7846424 --- /dev/null +++ b/assembler/list.h @@ -0,0 +1,296 @@ +/* + * Copied from the Linux kernel source tree, version 2.6.0-test1. + * + * Licensed under the GPL v2 as per the whole kernel source tree. + * + */ + +#ifndef _LIST_H +#define _LIST_H + +#ifndef __GNUC__ +# error "Need GNU GCC" +#endif + +#define typeof __typeof__ +#define offsetof(type, member) __builtin_offsetof (type, member) + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H */ diff --git a/assembler/main.c b/assembler/main.c new file mode 100644 index 0000000..bda23d0 --- /dev/null +++ b/assembler/main.c @@ -0,0 +1,1068 @@ +/* + * Copyright (C) 2006-2007 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "main.h" +#include "list.h" +#include "util.h" +#include "parser.h" +#include "args.h" +#include "initvals.h" + +#include +#include +#include + + +extern int yyparse(void); +extern int yydebug; + +struct file infile; +const char *infile_name; +const char *outfile_name; + + +struct out_operand { + enum { + OUTOPER_NORMAL, + OUTOPER_LABELREF, + } type; + + union { + unsigned int operand; /* For NORMAL */ + struct label *label; /* For LABELREF */ + } u; +}; + +struct code_output { + enum { + OUT_INSN, + OUT_LABEL, + } type; + + unsigned int opcode; + struct out_operand operands[3]; + + /* The absolute address of this instruction. + * Only used in resolve_labels(). */ + unsigned int address; + + const char *labelname; /* only for OUT_LABEL */ + /* Set to 1, if this is the %start instruction. */ + int is_start_insn; + + struct list_head list; +}; + +struct assembler_context { + int arch; + struct label *start_label; + + /* Tracking stuff */ + struct statement *cur_stmt; + + struct list_head output; +}; + + +#define for_each_statement(ctx, s) \ + list_for_each_entry(s, &infile.sl, list) { \ + ctx->cur_stmt = s; + +#define for_each_statement_end(ctx, s) \ + } do { ctx->cur_stmt = NULL; } while (0) + +#define _msg_helper(type, stmt, msg, x...) do { \ + fprintf(stderr, "Assembler " type); \ + if (stmt) { \ + fprintf(stderr, " (file \"%s\", line %u)", \ + stmt->info.file, \ + stmt->info.lineno); \ + } \ + fprintf(stderr, ":\n " msg "\n" ,##x); \ + } while (0) + +#define asm_error(ctx, msg, x...) do { \ + _msg_helper("ERROR", (ctx)->cur_stmt, msg ,##x); \ + exit(1); \ + } while (0) + +#define asm_warn(ctx, msg, x...) \ + _msg_helper("warning", (ctx)->cur_stmt, msg ,##x) + +#define asm_info(ctx, msg, x...) \ + _msg_helper("info", (ctx)->cur_stmt, msg ,##x) + + +static void eval_directives(struct assembler_context *ctx) +{ + struct statement *s; + struct asmdir *ad; + struct label *l; + int have_start_label = 0; + int have_arch = 0; + + for_each_statement(ctx, s) { + if (s->type == STMT_ASMDIR) { + ad = s->u.asmdir; + switch (ad->type) { + case ADIR_ARCH: + if (have_arch) + asm_error(ctx, "Multiple %%arch definitions"); + ctx->arch = ad->u.arch; + have_arch = 1; + break; + case ADIR_START: + if (have_start_label) + asm_error(ctx, "Multiple %%start definitions"); + ctx->start_label = ad->u.start; + have_start_label = 1; + break; + default: + asm_error(ctx, "Unknown ASM directive"); + } + } + } for_each_statement_end(ctx, s); + + if (!have_arch) + asm_error(ctx, "No %%arch defined"); + if (ctx->arch != NEWWORLD) + asm_error(ctx, "TODO: Only NEWWORLD arch supported, yet"); + if (!have_start_label) + asm_info(ctx, "Using start address 0"); +} + +static int is_possible_imm(unsigned int imm) +{ + unsigned int mask; + + /* Immediates are only possible up to 16bit (wordsize). */ + mask = ~0; + mask <<= 16; + if (imm & (1 << 15)) { + if ((imm & mask) != mask && + (imm & mask) != 0) + return 0; + } else { + if ((imm & mask) != 0) + return 0; + } + + return 1; +} + +static int is_valid_imm(unsigned int imm) +{ + unsigned int mask; + + /* This function checks if the immediate value is representable + * as a native immediate operand. + * + * The value itself is 10bit long, signed. + * We also honor sign-extension, so we allow values + * of 0xFFFF, for example. + */ + + if (!is_possible_imm(imm)) + return 0; + imm &= 0xFFFF; + + /* assert sign extension */ + mask = 0xFC00; + if (imm & (1 << 9)) { + /* sign-extended */ + if ((imm & mask) != mask) + return 0; + } else { + if (imm & mask) + return 0; + } + + return 1; +} + +static int is_contiguous_bitmask(unsigned int mask) +{ + int bit; + int only_zero_now = 0; + + /* This checks if the mask is contiguous. + * A contiguous mask is: + * 0b0001111110000 + * A non-contiguous mask is: + * 0b0001101110000 + */ + + bit = ffs(mask); + if (!bit) + return 1; + if (bit > 16) + return 1; + bit--; + for ( ; bit < 16; bit++) { + if (mask & (1 << bit)) { + if (only_zero_now) + return 0; + } else + only_zero_now = 1; + } + + return 1; +} + +static unsigned int generate_imm_operand(struct assembler_context *ctx, + const struct immediate *imm) +{ + unsigned int val, tmp; + unsigned int mask; + int too_long = 0; + + /* format: 0b11ii iiii iiii */ + + val = 0xC00; + tmp = imm->imm; + + if (!is_valid_imm(tmp)) { + asm_warn(ctx, "IMMEDIATE 0x%X (%d) too long " + "(> 9 bits + sign). Did you intend to " + "use implicit sign extension?", + tmp, (int)tmp); + } + + tmp &= 0x3FF; + val |= tmp; + + return val; +} + +static unsigned int generate_reg_operand(struct assembler_context *ctx, + const struct registr *reg) +{ + unsigned int val = 0; + + switch (reg->type) { + case GPR: + /* format: 0b1011 11rr rrrr */ + val |= 0xBC0; + if (reg->nr & ~0x3F) + asm_error(ctx, "GPR-nr too big"); + val |= reg->nr; + break; + case SPR: + /* format: 0b100. .... .... */ + val |= 0x800; + if (reg->nr & ~0x1FF) + asm_error(ctx, "SPR-nr too big"); + val |= reg->nr; + break; + case OFFR: + /* format: 0b1000 0110 0rrr */ + val |= 0x860; + if (reg->nr & ~0x7) + asm_error(ctx, "OFFR-nr too big"); + val |= reg->nr; + break; + default: + asm_error(ctx, "generate_reg_operand() regtype"); + } + + return val; +} + +static unsigned int generate_mem_operand(struct assembler_context *ctx, + const struct memory *mem) +{ + unsigned int val = 0, off, reg; + + switch (mem->type) { + case MEM_DIRECT: + /* format: 0b0mmm mmmm mmmm */ + off = mem->offset; + if (off & ~0x7FF) { + asm_warn(ctx, "DIRECT memoffset 0x%X too long (> 11 bits)", off); + off &= 0x7FF; + } + val |= off; + break; + case MEM_INDIRECT: + /* format: 0b101r rroo oooo */ + off = mem->offset; + reg = mem->offr_nr; + val |= 0xA00; + if (off & ~0x3F) { + asm_warn(ctx, "INDIRECT memoffset 0x%X too long (> 6 bits)", off); + off &= 0x3F; + } + if (reg & ~0x7) + asm_error(ctx, "OFFR-nr too big"); + val |= off; + val |= (reg << 6); + break; + default: + asm_error(ctx, "generate_mem_operand() memtype"); + } + + return val; +} + +static void generate_operand(struct assembler_context *ctx, + const struct operand *oper, + struct out_operand *out) +{ + out->type = OUTOPER_NORMAL; + + switch (oper->type) { + case OPER_IMM: + out->u.operand = generate_imm_operand(ctx, oper->u.imm); + break; + case OPER_REG: + out->u.operand = generate_reg_operand(ctx, oper->u.reg); + break; + case OPER_MEM: + out->u.operand = generate_mem_operand(ctx, oper->u.mem); + break; + case OPER_LABEL: + out->type = OUTOPER_LABELREF; + out->u.label = oper->u.label; + break; + case OPER_ADDR: + out->u.operand = oper->u.addr->addr; + break; + case OPER_RAW: + out->u.operand = oper->u.raw; + break; + default: + asm_error(ctx, "generate_operand() operstate"); + } +} + +static void do_assemble_insn(struct assembler_context *ctx, + struct instruction *insn, + unsigned int opcode) +{ + int i; + struct operlist *ol; + int nr_oper = 0; + uint64_t code = 0; + struct code_output *out; + struct label *labelref = NULL; + struct operand *oper; + int have_spr_operand = 0; + int have_mem_operand = 0; + + out = xmalloc(sizeof(*out)); + INIT_LIST_HEAD(&out->list); + out->opcode = opcode; + + ol = insn->operands; + if (ARRAY_SIZE(out->operands) > ARRAY_SIZE(ol->oper)) + asm_error(ctx, "Internal operand array confusion"); + + for (i = 0; i < ARRAY_SIZE(out->operands); i++) { + oper = ol->oper[i]; + if (!oper) + continue; + + /* If this is an INPUT operand (first or second), we must + * make sure that not both are accessing SPR or MEMORY. + * The device only supports one SPR or MEMORY operand in + * the input operands. */ + if ((i == 0) || (i == 1)) { + if ((oper->type == OPER_REG) && + (oper->u.reg->type == SPR)) { + if (have_spr_operand) + asm_error(ctx, "Multiple SPR input operands in one instruction"); + have_spr_operand = 1; + } + if (oper->type == OPER_MEM) { + if (have_mem_operand) + asm_error(ctx, "Multiple MEMORY input operands in on instruction"); + have_mem_operand = 1; + } + } + + generate_operand(ctx, oper, &out->operands[i]); + nr_oper++; + } + if (nr_oper != 3) + asm_error(ctx, "Internal error: nr_oper at " + "lowlevel do_assemble_insn"); + + list_add_tail(&out->list, &ctx->output); +} + +static unsigned int merge_ext_into_opcode(struct assembler_context *ctx, + unsigned int opbase, + struct instruction *insn) +{ + struct operlist *ol; + unsigned int opcode; + unsigned int mask, shift; + + ol = insn->operands; + opcode = opbase; + mask = ol->oper[0]->u.raw; + if (mask & ~0xF) + asm_error(ctx, "opcode MASK extension too big (> 0xF)"); + shift = ol->oper[1]->u.raw; + if (shift & ~0xF) + asm_error(ctx, "opcode SHIFT extension too big (> 0xF)"); + opcode |= (mask << 4); + opcode |= shift; + ol->oper[0] = ol->oper[2]; + ol->oper[1] = ol->oper[3]; + ol->oper[2] = ol->oper[4]; + + return opcode; +} + +static unsigned int merge_external_jmp_into_opcode(struct assembler_context *ctx, + unsigned int opbase, + struct instruction *insn) +{ + struct operlist *ol; + unsigned int cond; + unsigned int opcode; + + ol = insn->operands; + opcode = opbase; + cond = ol->oper[0]->u.raw; + if (cond & ~0xFF) + asm_error(ctx, "External jump condition value too big (> 0xFF)"); + opcode |= cond; + ol->oper[0] = ol->oper[1]; + ol->oper[1] = ol->oper[2]; + ol->oper[2] = ol->oper[3]; + + return opcode; +} + +static void assemble_instruction(struct assembler_context *ctx, + struct instruction *insn); + +static void emulate_mov_insn(struct assembler_context *ctx, + struct instruction *insn) +{ + struct instruction em_insn; + struct operlist em_ol; + struct operand em_op_shift; + struct operand em_op_mask; + struct operand em_op_x; + struct operand em_op_y; + struct immediate em_imm_x; + struct immediate em_imm_y; + + struct operand *in, *out; + unsigned int tmp; + + /* This is a pseudo-OP. We emulate it by OR or ORX */ + + in = insn->operands->oper[0]; + out = insn->operands->oper[1]; + + em_insn.op = OP_OR; + em_ol.oper[0] = in; + em_imm_x.imm = 0; + em_op_x.type = OPER_IMM; + em_op_x.u.imm = &em_imm_x; + em_ol.oper[1] = &em_op_x; + em_ol.oper[2] = out; + + if (in->type == OPER_IMM) { + tmp = in->u.imm->imm; + if (!is_possible_imm(tmp)) + asm_error(ctx, "MOV operand 0x%X > 16bit", tmp); + if (!is_valid_imm(tmp)) { + /* Immediate too big for plain OR */ + em_insn.op = OP_ORX; + + em_op_mask.type = OPER_RAW; + em_op_mask.u.raw = 0x7; + em_op_shift.type = OPER_RAW; + em_op_shift.u.raw = 0x8; + + em_imm_x.imm = (tmp & 0xFF00) >> 8; + em_op_x.type = OPER_IMM; + em_op_x.u.imm = &em_imm_x; + + em_imm_y.imm = (tmp & 0x00FF); + em_op_y.type = OPER_IMM; + em_op_y.u.imm = &em_imm_y; + + em_ol.oper[0] = &em_op_mask; + em_ol.oper[1] = &em_op_shift; + em_ol.oper[2] = &em_op_x; + em_ol.oper[3] = &em_op_y; + em_ol.oper[4] = out; + } + } + + em_insn.operands = &em_ol; + assemble_instruction(ctx, &em_insn); /* recurse */ +} + +static void emulate_jmp_insn(struct assembler_context *ctx, + struct instruction *insn) +{ + struct instruction em_insn; + struct operlist em_ol; + struct operand em_op; + struct immediate em_imm; + + /* This is a pseudo-OP. We emulate it by JE */ + + em_insn.op = OP_JE; + em_imm.imm = 1; + em_op.type = OPER_IMM; + em_op.u.imm = &em_imm; + em_ol.oper[0] = &em_op; + em_ol.oper[1] = &em_op; + em_ol.oper[2] = insn->operands->oper[0]; + em_insn.operands = &em_ol; + assemble_instruction(ctx, &em_insn); /* recurse */ +} + +static void emulate_jand_insn(struct assembler_context *ctx, + struct instruction *insn, + int inverted) +{ + struct instruction em_insn; + struct operlist em_ol; + struct operand em_op_shift; + struct operand em_op_mask; + struct operand em_op_y; + struct immediate em_imm; + + struct operand *oper0, *oper1, *oper2; + struct operand *imm_oper = NULL; + unsigned int tmp; + int first_bit, last_bit; + + oper0 = insn->operands->oper[0]; + oper1 = insn->operands->oper[1]; + oper2 = insn->operands->oper[2]; + + if (oper0->type == OPER_IMM) + imm_oper = oper0; + if (oper1->type == OPER_IMM) + imm_oper = oper1; + if (oper0->type == OPER_IMM && oper1->type == OPER_IMM) + imm_oper = NULL; + + if (imm_oper) { + /* We have a single immediate operand. + * Check if it's representable by a normal JAND insn. + */ + tmp = imm_oper->u.imm->imm; + if (!is_valid_imm(tmp)) { + /* Nope, this must be emulated by JZX/JNZX */ + if (!is_contiguous_bitmask(tmp)) { + asm_error(ctx, "Long bitmask 0x%X is not contiguous", + tmp); + } + + first_bit = ffs(tmp); + last_bit = ffs(~(tmp >> (first_bit - 1))) - 1 + first_bit - 1; + + if (inverted) + em_insn.op = OP_JZX; + else + em_insn.op = OP_JNZX; + em_op_shift.type = OPER_RAW; + em_op_shift.u.raw = first_bit - 1; + em_op_mask.type = OPER_RAW; + em_op_mask.u.raw = last_bit - first_bit; + + em_imm.imm = 0; + em_op_y.type = OPER_IMM; + em_op_y.u.imm = &em_imm; + + em_ol.oper[0] = &em_op_mask; + em_ol.oper[1] = &em_op_shift; + if (oper0->type != OPER_IMM) + em_ol.oper[2] = oper0; + else + em_ol.oper[2] = oper1; + em_ol.oper[3] = &em_op_y; + em_ol.oper[4] = oper2; + + em_insn.operands = &em_ol; + + assemble_instruction(ctx, &em_insn); /* recurse */ + return; + } + } + + /* Do a normal JAND/JNAND instruction */ + if (inverted) + do_assemble_insn(ctx, insn, 0x040 | 0x1); + else + do_assemble_insn(ctx, insn, 0x040); +} + +static void assemble_instruction(struct assembler_context *ctx, + struct instruction *insn) +{ + unsigned int opcode; + + switch (insn->op) { + case OP_ADD: + do_assemble_insn(ctx, insn, 0x1C0); + break; + case OP_ADDSC: + do_assemble_insn(ctx, insn, 0x1C2); + break; + case OP_ADDC: + do_assemble_insn(ctx, insn, 0x1C1); + break; + case OP_ADDSCC: + do_assemble_insn(ctx, insn, 0x1C3); + break; + case OP_SUB: + do_assemble_insn(ctx, insn, 0x1D0); + break; + case OP_SUBSC: + do_assemble_insn(ctx, insn, 0x1D2); + break; + case OP_SUBC: + do_assemble_insn(ctx, insn, 0x1D1); + break; + case OP_SUBSCC: + do_assemble_insn(ctx, insn, 0x1D3); + break; + case OP_SRA: + do_assemble_insn(ctx, insn, 0x130); + break; + case OP_OR: + do_assemble_insn(ctx, insn, 0x160); + break; + case OP_AND: + do_assemble_insn(ctx, insn, 0x140); + break; + case OP_XOR: + do_assemble_insn(ctx, insn, 0x170); + break; + case OP_SR: + do_assemble_insn(ctx, insn, 0x120); + break; + case OP_SRX: + opcode = merge_ext_into_opcode(ctx, 0x200, insn); + do_assemble_insn(ctx, insn, opcode); + break; + case OP_SL: + do_assemble_insn(ctx, insn, 0x110); + break; + case OP_RL: + do_assemble_insn(ctx, insn, 0x1A0); + break; + case OP_RR: + do_assemble_insn(ctx, insn, 0x1B0); + break; + case OP_NAND: + do_assemble_insn(ctx, insn, 0x150); + break; + case OP_ORX: + opcode = merge_ext_into_opcode(ctx, 0x300, insn); + do_assemble_insn(ctx, insn, opcode); + break; + case OP_MOV: + emulate_mov_insn(ctx, insn); + return; + case OP_JMP: + emulate_jmp_insn(ctx, insn); + return; + case OP_JAND: + emulate_jand_insn(ctx, insn, 0); + return; + case OP_JNAND: + emulate_jand_insn(ctx, insn, 1); + return; + case OP_JS: + do_assemble_insn(ctx, insn, 0x050); + break; + case OP_JNS: + do_assemble_insn(ctx, insn, 0x050 | 0x1); + break; + case OP_JE: + do_assemble_insn(ctx, insn, 0x0D0); + break; + case OP_JNE: + do_assemble_insn(ctx, insn, 0x0D0 | 0x1); + break; + case OP_JLS: + do_assemble_insn(ctx, insn, 0x0D2); + break; + case OP_JGES: + do_assemble_insn(ctx, insn, 0x0D2 | 0x1); + break; + case OP_JGS: + do_assemble_insn(ctx, insn, 0x0D4); + break; + case OP_JLES: + do_assemble_insn(ctx, insn, 0x0D4 | 0x1); + break; + case OP_JL: + do_assemble_insn(ctx, insn, 0x0DA); + break; + case OP_JGE: + do_assemble_insn(ctx, insn, 0x0DA | 0x1); + break; + case OP_JG: + do_assemble_insn(ctx, insn, 0x0DC); + break; + case OP_JLE: + do_assemble_insn(ctx, insn, 0x0DC | 0x1); + break; + case OP_JZX: + opcode = merge_ext_into_opcode(ctx, 0x400, insn); + do_assemble_insn(ctx, insn, opcode); + break; + case OP_JNZX: + opcode = merge_ext_into_opcode(ctx, 0x500, insn); + do_assemble_insn(ctx, insn, opcode); + break; + case OP_JEXT: + opcode = merge_external_jmp_into_opcode(ctx, 0x700, insn); + do_assemble_insn(ctx, insn, opcode); + break; + case OP_JNEXT: + opcode = merge_external_jmp_into_opcode(ctx, 0x600, insn); + do_assemble_insn(ctx, insn, opcode); + break; + case OP_CALL: + do_assemble_insn(ctx, insn, 0x002); + break; + case OP_RET: + do_assemble_insn(ctx, insn, 0x003); + break; + case OP_TKIPH: + case OP_TKIPHS: + case OP_TKIPL: + case OP_TKIPLS: + do_assemble_insn(ctx, insn, 0x1E0); + break; + case OP_NAP: + do_assemble_insn(ctx, insn, 0x001); + break; + case RAW_CODE: + do_assemble_insn(ctx, insn, insn->opcode); + break; + default: + asm_error(ctx, "Unknown op"); + } +} + +static void assemble_instructions(struct assembler_context *ctx) +{ + struct statement *s; + struct instruction *insn; + struct code_output *out; + + if (ctx->start_label) { + /* Generate a jump instruction at offset 0 to + * jump to the code start. + */ + struct instruction sjmp; + struct operlist ol; + struct operand oper; + + oper.type = OPER_LABEL; + oper.u.label = ctx->start_label; + ol.oper[0] = &oper; + sjmp.op = OP_JMP; + sjmp.operands = &ol; + + assemble_instruction(ctx, &sjmp); + out = list_entry(ctx->output.next, struct code_output, list); + out->is_start_insn = 1; + } + + for_each_statement(ctx, s) { + switch (s->type) { + case STMT_INSN: + ctx->cur_stmt = s; + insn = s->u.insn; + assemble_instruction(ctx, insn); + break; + case STMT_LABEL: + out = xmalloc(sizeof(*out)); + INIT_LIST_HEAD(&out->list); + out->type = OUT_LABEL; + out->labelname = s->u.label->name; + + list_add_tail(&out->list, &ctx->output); + break; + case STMT_ASMDIR: + break; + } + } for_each_statement_end(ctx, s); +} + +/* Resolve a label reference to the address it points to. */ +static int get_labeladdress(struct assembler_context *ctx, + struct code_output *this_insn, + struct label *labelref) +{ + struct code_output *c; + bool found = 0; + int address = -1; + + switch (labelref->direction) { + case LABELREF_ABSOLUTE: + list_for_each_entry(c, &ctx->output, list) { + if (c->type != OUT_LABEL) + continue; + if (strcmp(c->labelname, labelref->name) != 0) + continue; + if (found) { + asm_error(ctx, "Ambiguous label reference \"%s\"", + labelref->name); + } + found = 1; + address = c->address; + } + break; + case LABELREF_RELATIVE_BACK: + for (c = list_entry(this_insn->list.prev, typeof(*c), list); + &c->list != &ctx->output; + c = list_entry(c->list.prev, typeof(*c), list)) { + if (c->type != OUT_LABEL) + continue; + if (strcmp(c->labelname, labelref->name) == 0) { + /* Found */ + address = c->address; + break; + } + } + break; + case LABELREF_RELATIVE_FORWARD: + for (c = list_entry(this_insn->list.next, typeof(*c), list); + &c->list != &ctx->output; + c = list_entry(c->list.next, typeof(*c), list)) { + if (c->type != OUT_LABEL) + continue; + if (strcmp(c->labelname, labelref->name) == 0) { + /* Found */ + address = c->address; + break; + } + } + break; + } + + return address; +} + +static void resolve_labels(struct assembler_context *ctx) +{ + struct code_output *c; + int addr; + int i; + unsigned int current_address; + + /* Calculate the absolute addresses for each instruction. */ +recalculate_addresses: + current_address = 0; + list_for_each_entry(c, &ctx->output, list) { + switch (c->type) { + case OUT_INSN: + c->address = current_address; + current_address++; + break; + case OUT_LABEL: + c->address = current_address; + break; + } + } + + /* Resolve the symbolic label references. */ + list_for_each_entry(c, &ctx->output, list) { + switch (c->type) { + case OUT_INSN: + if (c->is_start_insn) { + /* If the first %start-jump jumps to 001, we can + * optimize it away, as it's unneeded. + */ + i = 2; + if (c->operands[i].type != OUTOPER_LABELREF) + asm_error(ctx, "Internal error, %%start insn oper 2 not labelref"); + if (c->operands[i].u.label->direction != LABELREF_ABSOLUTE) + asm_error(ctx, "%%start label reference not absolute"); + addr = get_labeladdress(ctx, c, c->operands[i].u.label); + if (addr < 0) + goto does_not_exist; + if (addr == 1) { + list_del(&c->list); /* Kill it */ + goto recalculate_addresses; + } + } + + for (i = 0; i < ARRAY_SIZE(c->operands); i++) { + if (c->operands[i].type != OUTOPER_LABELREF) + continue; + addr = get_labeladdress(ctx, c, c->operands[i].u.label); + if (addr < 0) + goto does_not_exist; + c->operands[i].u.operand = addr; + if (i != 2) /* Is not a jump target */ + c->operands[i].u.operand |= 0xC00; /* Make it be an immediate */ + } + break; + case OUT_LABEL: + break; + } + } + + return; +does_not_exist: + asm_error(ctx, "Label \"%s\" does not exist", + c->operands[i].u.label->name); +} + +static void emit_code(struct assembler_context *ctx) +{ + FILE *fd; + char *fn; + size_t fn_len; + struct code_output *c; + uint64_t code; + unsigned char outbuf[8]; + unsigned int insn_count = 0; + struct fw_header hdr; + + fn_len = strlen(outfile_name) + 20; + fn = xmalloc(fn_len); + snprintf(fn, fn_len, "%s.ucode", outfile_name); + fd = fopen(fn, "w+"); + if (!fd) { + fprintf(stderr, "Could not open microcode output file \"%s\"\n", fn); + free(fn); + exit(1); + } + if (IS_VERBOSE_DEBUG) + fprintf(stderr, "\nCode:\n"); + + list_for_each_entry(c, &ctx->output, list) { + switch (c->type) { + case OUT_INSN: + insn_count++; + break; + default: + break; + } + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.type = FW_TYPE_UCODE; + hdr.ver = FW_HDR_VER; + hdr.size = cpu_to_be32(8 * insn_count); + if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) { + fprintf(stderr, "Could not write microcode outfile\n"); + exit(1); + } + + if (insn_count > NUM_INSN_LIMIT) + asm_warn(ctx, "Generating more than %d instructions. This " + "will overflow the device microcode memory.", + NUM_INSN_LIMIT); + + list_for_each_entry(c, &ctx->output, list) { + switch (c->type) { + case OUT_INSN: + if (IS_VERBOSE_DEBUG) { + fprintf(stderr, "%03X %03X,%03X,%03X\n", + c->opcode, + c->operands[0].u.operand, + c->operands[1].u.operand, + c->operands[2].u.operand); + } + code = 0; + + /* Instruction binary format is: xxyy yzzz 0000 oooX + * Big-Endian, X is the most significant part of Xxx. + */ + code |= (c->opcode << 4); + + code |= (((uint64_t)c->operands[0].u.operand & 0xF00) >> 8); + code |= (((uint64_t)c->operands[0].u.operand & 0x0FF) << 56); + + code |= ((uint64_t)c->operands[1].u.operand << 44); + + code |= ((uint64_t)c->operands[2].u.operand << 32); + + outbuf[7] = (code & 0x00000000000000FFULL); + outbuf[6] = (code & 0x000000000000FF00ULL) >> 8; + outbuf[5] = (code & 0x0000000000FF0000ULL) >> 16; + outbuf[4] = (code & 0x00000000FF000000ULL) >> 24; + outbuf[3] = (code & 0x000000FF00000000ULL) >> 32; + outbuf[2] = (code & 0x0000FF0000000000ULL) >> 40; + outbuf[1] = (code & 0x00FF000000000000ULL) >> 48; + outbuf[0] = (code & 0xFF00000000000000ULL) >> 56; + + if (fwrite(&outbuf, ARRAY_SIZE(outbuf), 1, fd) != 1) { + fprintf(stderr, "Could not write microcode outfile\n"); + exit(1); + } + break; + case OUT_LABEL: + break; + } + } + fclose(fd); + free(fn); +} + +static void assemble(void) +{ + struct assembler_context ctx; + + memset(&ctx, 0, sizeof(ctx)); + INIT_LIST_HEAD(&ctx.output); + + eval_directives(&ctx); + assemble_instructions(&ctx); + resolve_labels(&ctx); + emit_code(&ctx); +} + +static void initialize(void) +{ + INIT_LIST_HEAD(&infile.sl); + INIT_LIST_HEAD(&infile.ivals); +#ifdef YYDEBUG + if (IS_INSANE_DEBUG) + yydebug = 1; + else + yydebug = 0; +#endif /* YYDEBUG */ +} + +int main(int argc, char **argv) +{ + int err, res = 1; + + err = parse_args(argc, argv); + if (err) + goto out; + err = open_input_file(); + if (err) + goto out; + initialize(); + yyparse(); + assemble(); + assemble_initvals(); + close_input_file(); + res = 0; +out: + /* Lazyman simply leaks all allocated memory. */ + return res; +} diff --git a/assembler/main.h b/assembler/main.h new file mode 100644 index 0000000..d309577 --- /dev/null +++ b/assembler/main.h @@ -0,0 +1,154 @@ +#ifndef BCM43xx_ASM_MAIN_H_ +#define BCM43xx_ASM_MAIN_H_ + +#include + +#include "list.h" +#include "util.h" + + +/* The header that fwcutter also puts in to every .fw file */ +struct fw_header { + /* Type of the firmware data */ + uint8_t type; + /* Version number of the firmware data format */ + uint8_t ver; + uint8_t __padding[2]; + /* Size of the data. For ucode and PCM this is in bytes. + * For IV this is in number-of-ivs. (big-endian!) */ + be32_t size; +} __attribute__ ((__packed__)); + +/* struct fw_header -> type */ +#define FW_TYPE_UCODE 'u' +#define FW_TYPE_PCM 'p' +#define FW_TYPE_IV 'i' +/* struct fw_header -> ver */ +#define FW_HDR_VER 0x01 + + +/* Maximum number of allowed instructions in the code output. + * This is what device memory can hold at maximum. + */ +#define NUM_INSN_LIMIT 4096 + + +struct lineinfo { + char file[64]; + char linecopy[128]; + unsigned int lineno; + unsigned int column; +}; +extern struct lineinfo cur_lineinfo; + + +struct immediate { + unsigned int imm; +}; + +struct address { + unsigned int addr; +}; + +struct registr { + int type; /* SPR, GPR or OFFR */ + unsigned int nr; +}; + +struct memory { + enum { + MEM_DIRECT, + MEM_INDIRECT, + } type; + /* Offset immediate */ + unsigned int offset; + /* Offset Register number (only MEM_INDIRECT) */ + unsigned int offr_nr; +}; + +struct label { + const char *name; + + /* direction is only used for label references. */ + enum { + LABELREF_ABSOLUTE, + LABELREF_RELATIVE_BACK, + LABELREF_RELATIVE_FORWARD, + } direction; +}; + +struct operand { + enum { + OPER_IMM, + OPER_REG, + OPER_MEM, + OPER_LABEL, + OPER_ADDR, + OPER_RAW, + } type; + union { + struct immediate *imm; + struct registr *reg; + struct memory *mem; + struct label *label; + struct address *addr; + unsigned int raw; + } u; +}; + +struct operlist { + struct operand *oper[5]; +}; + +struct instruction { + int op; + struct operlist *operands; + unsigned int opcode; /* only for RAW */ +}; + +struct asmdir { + enum { + ADIR_ARCH, + ADIR_START, + } type; + union { + enum { + OLDWORLD, + NEWWORLD, + } arch; + struct label *start; + } u; +}; + +struct statement { + enum { + STMT_INSN, + STMT_LABEL, + STMT_ASMDIR, + } type; + union { + struct instruction *insn; + struct label *label; + struct asmdir *asmdir; + } u; + struct lineinfo info; + + struct list_head list; +}; + + +struct file { + /* The (microcode) statement list */ + struct list_head sl; + /* The initvals sections list */ + struct list_head ivals; + /* The file descriptor */ + int fd; +}; + + +extern struct file infile; +extern const char *infile_name; +extern const char *outfile_name; + +#endif /* BCM43xx_ASM_MAIN_H_ */ diff --git a/assembler/parser.y b/assembler/parser.y new file mode 100644 index 0000000..753be08 --- /dev/null +++ b/assembler/parser.y @@ -0,0 +1,1314 @@ +%{ + +/* + * Copyright (C) 2006-2007 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "main.h" +#include "initvals.h" +#include "util.h" + +#include +#include +#include +#include + +extern char *yytext; +extern void yyerror(char *); +extern int yyparse(void); +extern int yylex(void); + +static struct operand * store_oper_sanity(struct operand *oper); + +/* The current .section */ +extern int section; +/* Pointer to the current initvals section data structure. */ +extern struct initvals_sect *cur_initvals_sect; + +%} + +%token SECTION_TEXT SECTION_IVALS + +%token ASM_ARCH ASM_START SPR GPR OFFR LR COMMA BRACK_OPEN BRACK_CLOSE PAREN_OPEN PAREN_CLOSE HEXNUM DECNUM ARCH_NEWWORLD ARCH_OLDWORLD LABEL IDENT LABELREF + +%token PLUS MINUS MULTIPLY DIVIDE BITW_OR BITW_AND BITW_XOR BITW_NOT LEFTSHIFT RIGHTSHIFT + +%token OP_ADD OP_ADDSC OP_ADDC OP_ADDSCC OP_SUB OP_SUBSC OP_SUBC OP_SUBSCC OP_SRA OP_OR OP_AND OP_XOR OP_SR OP_SRX OP_SL OP_RL OP_RR OP_NAND OP_ORX OP_MOV OP_JMP OP_JAND OP_JNAND OP_JS OP_JNS OP_JE OP_JNE OP_JLS OP_JGES OP_JGS OP_JLES OP_JL OP_JGE OP_JG OP_JLE OP_JZX OP_JNZX OP_JEXT OP_JNEXT OP_CALL OP_RET OP_TKIPH OP_TKIPHS OP_TKIPL OP_TKIPLS OP_NAP RAW_CODE + +%token IVAL_MMIO16 IVAL_MMIO32 IVAL_PHY IVAL_RADIO IVAL_SHM16 IVAL_SHM32 + +%start line + +%% + +line : /* empty */ + | line statement { + struct statement *s = $2; + if (section != SECTION_TEXT) + yyerror("Microcode text instruction in non .text section"); + memcpy(&s->info, &cur_lineinfo, sizeof(struct lineinfo)); + list_add_tail(&s->list, &infile.sl); + } + | line section_switch { + } + | line ivals_write { + struct initval_op *io = $2; + if (section != SECTION_IVALS) + yyerror("InitVals write in non .initvals section"); + memcpy(&io->info, &cur_lineinfo, sizeof(struct lineinfo)); + INIT_LIST_HEAD(&io->list); + list_add_tail(&io->list, &cur_initvals_sect->ops); + } + ; + +section_switch : SECTION_TEXT { + section = SECTION_TEXT; + } + | SECTION_IVALS PAREN_OPEN identifier PAREN_CLOSE { + const char *sectname = $3; + struct initvals_sect *s; + cur_initvals_sect = NULL; + /* Search if there already is a section by that name. */ + list_for_each_entry(s, &infile.ivals, list) { + if (strcmp(sectname, s->name) == 0) + cur_initvals_sect = s; + } + if (!cur_initvals_sect) { + /* Not found, create a new one. */ + s = xmalloc(sizeof(struct initvals_sect)); + s->name = sectname; + INIT_LIST_HEAD(&s->ops); + INIT_LIST_HEAD(&s->list); + list_add_tail(&s->list, &infile.ivals); + cur_initvals_sect = s; + } + section = SECTION_IVALS; + } + ; + +ivals_write : IVAL_MMIO16 hexnum_decnum COMMA hexnum_decnum { + struct initval_op *iop = xmalloc(sizeof(struct initval_op)); + iop->type = IVAL_W_MMIO16; + iop->args[0] = (unsigned int)(unsigned long)$2; + iop->args[1] = (unsigned int)(unsigned long)$4; + $$ = iop; + } + | IVAL_MMIO32 hexnum_decnum COMMA hexnum_decnum { + struct initval_op *iop = xmalloc(sizeof(struct initval_op)); + iop->type = IVAL_W_MMIO32; + iop->args[0] = (unsigned int)(unsigned long)$2; + iop->args[1] = (unsigned int)(unsigned long)$4; + $$ = iop; + } + | IVAL_PHY hexnum_decnum COMMA hexnum_decnum { + struct initval_op *iop = xmalloc(sizeof(struct initval_op)); + iop->type = IVAL_W_PHY; + iop->args[0] = (unsigned int)(unsigned long)$2; + iop->args[1] = (unsigned int)(unsigned long)$4; + $$ = iop; + } + | IVAL_RADIO hexnum_decnum COMMA hexnum_decnum { + struct initval_op *iop = xmalloc(sizeof(struct initval_op)); + iop->type = IVAL_W_RADIO; + iop->args[0] = (unsigned int)(unsigned long)$2; + iop->args[1] = (unsigned int)(unsigned long)$4; + $$ = iop; + } + | IVAL_SHM16 hexnum_decnum COMMA hexnum_decnum COMMA hexnum_decnum { + struct initval_op *iop = xmalloc(sizeof(struct initval_op)); + iop->type = IVAL_W_SHM16; + iop->args[0] = (unsigned int)(unsigned long)$2; + iop->args[1] = (unsigned int)(unsigned long)$4; + iop->args[2] = (unsigned int)(unsigned long)$6; + $$ = iop; + } + | IVAL_SHM32 hexnum_decnum COMMA hexnum_decnum COMMA hexnum_decnum { + struct initval_op *iop = xmalloc(sizeof(struct initval_op)); + iop->type = IVAL_W_SHM32; + iop->args[0] = (unsigned int)(unsigned long)$2; + iop->args[1] = (unsigned int)(unsigned long)$4; + iop->args[2] = (unsigned int)(unsigned long)$6; + $$ = iop; + } + ; + +statement : asmdir { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_ASMDIR; + s->u.asmdir = $1; + $$ = s; + } + | label { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_LABEL; + s->u.label = $1; + $$ = s; + } + | insn_add { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_addsc { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_addc { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_addscc { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_sub { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_subsc { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_subc { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_subscc { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_sra { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_or { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_and { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_xor { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_sr { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_srx { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_sl { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_rl { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_rr { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_nand { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_orx { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_mov { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jmp { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jand { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jnand { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_js { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jns { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_je { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jne { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jls { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jges { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jgs { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jles { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jl { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jge { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jg { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jle { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jzx { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jnzx { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jext { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_jnext { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_call { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_ret { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_tkiph { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_tkiphs { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_tkipl { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_tkipls { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_nap { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + | insn_raw { + struct statement *s = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&s->list); + s->type = STMT_INSN; + s->u.insn = $1; + $$ = s; + } + ; + +/* ASM directives */ +asmdir : asmarch { + $$ = $1; + } + | ASM_START identifier { + struct asmdir *ad = xmalloc(sizeof(struct asmdir)); + struct label *label = xmalloc(sizeof(struct label)); + label->name = $2; + label->direction = LABELREF_ABSOLUTE; + ad->type = ADIR_START; + ad->u.start = label; + $$ = ad; + } + ; + +asmarch : ASM_ARCH ARCH_NEWWORLD { + struct asmdir *ad = xmalloc(sizeof(struct asmdir)); + ad->type = ADIR_ARCH; + ad->u.arch = NEWWORLD; + $$ = ad; + } + | ASM_ARCH ARCH_OLDWORLD { + struct asmdir *ad = xmalloc(sizeof(struct asmdir)); + ad->type = ADIR_ARCH; + ad->u.arch = OLDWORLD; + $$ = ad; + } + ; + +label : LABEL { + struct label *label = xmalloc(sizeof(struct label)); + char *l; + l = xstrdup(yytext); + l[strlen(l) - 1] = '\0'; + label->name = l; + $$ = label; + } + ; + +/* add */ +insn_add : OP_ADD operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_ADD; + insn->operands = $2; + $$ = insn; + } + ; + +/* add. */ +insn_addsc : OP_ADDSC operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_ADDSC; + insn->operands = $2; + $$ = insn; + } + ; + +/* addc */ +insn_addc : OP_ADDC operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_ADDC; + insn->operands = $2; + $$ = insn; + } + ; + +/* addc. */ +insn_addscc : OP_ADDSCC operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_ADDSCC; + insn->operands = $2; + $$ = insn; + } + ; + +/* sub */ +insn_sub : OP_SUB operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SUB; + insn->operands = $2; + $$ = insn; + } + ; + +/* sub. */ +insn_subsc : OP_SUBSC operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SUBSC; + insn->operands = $2; + $$ = insn; + } + ; + +/* subc */ +insn_subc : OP_SUBC operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SUBC; + insn->operands = $2; + $$ = insn; + } + ; + +/* subc. */ +insn_subscc : OP_SUBSCC operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SUBSCC; + insn->operands = $2; + $$ = insn; + } + ; + +insn_sra : OP_SRA operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SRA; + insn->operands = $2; + $$ = insn; + } + ; + +insn_or : OP_OR operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_OR; + insn->operands = $2; + $$ = insn; + } + ; + +insn_and : OP_AND operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_AND; + insn->operands = $2; + $$ = insn; + } + ; + +insn_xor : OP_XOR operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_XOR; + insn->operands = $2; + $$ = insn; + } + ; + +insn_sr : OP_SR operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SR; + insn->operands = $2; + $$ = insn; + } + ; + +insn_srx : OP_SRX extended_operlist { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SRX; + insn->operands = $2; + $$ = insn; + } + ; + +insn_sl : OP_SL operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_SL; + insn->operands = $2; + $$ = insn; + } + ; + +insn_rl : OP_RL operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_RL; + insn->operands = $2; + $$ = insn; + } + ; + +insn_rr : OP_RR operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_RR; + insn->operands = $2; + $$ = insn; + } + ; + +insn_nand : OP_NAND operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_NAND; + insn->operands = $2; + $$ = insn; + } + ; + +insn_orx : OP_ORX extended_operlist { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_ORX; + insn->operands = $2; + $$ = insn; + } + ; + +insn_mov : OP_MOV operlist_2 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_MOV; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jmp : OP_JMP labelref { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = xmalloc(sizeof(struct operlist)); + ol->oper[0] = $2; + insn->op = OP_JMP; + insn->operands = ol; + $$ = insn; + } + ; + +insn_jand : OP_JAND operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JAND; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jnand : OP_JNAND operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JNAND; + insn->operands = $2; + $$ = insn; + } + ; + +insn_js : OP_JS operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JS; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jns : OP_JNS operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JNS; + insn->operands = $2; + $$ = insn; + } + ; + +insn_je : OP_JE operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JE; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jne : OP_JNE operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JNE; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jls : OP_JLS operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JLS; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jges : OP_JGES operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JGES; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jgs : OP_JGS operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JGS; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jles : OP_JLES operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JLES; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jl : OP_JL operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JL; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jge : OP_JGE operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JGE; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jg : OP_JG operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JG; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jle : OP_JLE operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JLE; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jzx : OP_JZX extended_operlist { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JZX; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jnzx : OP_JNZX extended_operlist { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JNZX; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jext : OP_JEXT external_jump_operands { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JEXT; + insn->operands = $2; + $$ = insn; + } + ; + +insn_jnext : OP_JNEXT external_jump_operands { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = OP_JNEXT; + insn->operands = $2; + $$ = insn; + } + ; + +linkreg : LR regnr { + $$ = $2; + } + ; + +insn_call : OP_CALL linkreg COMMA labelref { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = xmalloc(sizeof(struct operlist)); + struct operand *oper_lr = xmalloc(sizeof(struct operand)); + struct operand *oper_zero = xmalloc(sizeof(struct operand)); + oper_zero->type = OPER_RAW; + oper_zero->u.raw = 0; + oper_lr->type = OPER_RAW; + oper_lr->u.raw = (unsigned long)$2; + ol->oper[0] = oper_lr; + ol->oper[1] = oper_zero; + ol->oper[2] = $4; + insn->op = OP_CALL; + insn->operands = ol; + $$ = insn; + } + ; + +insn_ret : OP_RET linkreg COMMA linkreg { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = xmalloc(sizeof(struct operlist)); + struct operand *oper_lr0 = xmalloc(sizeof(struct operand)); + struct operand *oper_lr1 = xmalloc(sizeof(struct operand)); + struct operand *oper_zero = xmalloc(sizeof(struct operand)); + oper_zero->type = OPER_RAW; + oper_zero->u.raw = 0; + oper_lr0->type = OPER_RAW; + oper_lr0->u.raw = (unsigned long)$2; + oper_lr1->type = OPER_RAW; + oper_lr1->u.raw = (unsigned long)$4; + ol->oper[0] = oper_lr0; + ol->oper[1] = oper_zero; + ol->oper[2] = oper_lr1; + insn->op = OP_RET; + insn->operands = ol; + $$ = insn; + } + ; + +insn_tkiph : OP_TKIPH operlist_2 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = $2; + struct operand *flags = xmalloc(sizeof(struct operand)); + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = 0x1; + flags->type = OPER_IMM; + flags->u.imm = imm; + ol->oper[2] = ol->oper[1]; + ol->oper[1] = flags; + insn->op = OP_TKIPH; + insn->operands = ol; + $$ = insn; + } + ; + +insn_tkiphs : OP_TKIPHS operlist_2 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = $2; + struct operand *flags = xmalloc(sizeof(struct operand)); + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = 0x1 | 0x2; + flags->type = OPER_IMM; + flags->u.imm = imm; + ol->oper[2] = ol->oper[1]; + ol->oper[1] = flags; + insn->op = OP_TKIPH; + insn->operands = ol; + $$ = insn; + } + ; + +insn_tkipl : OP_TKIPL operlist_2 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = $2; + struct operand *flags = xmalloc(sizeof(struct operand)); + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = 0x0; + flags->type = OPER_IMM; + flags->u.imm = imm; + ol->oper[2] = ol->oper[1]; + ol->oper[1] = flags; + insn->op = OP_TKIPH; + insn->operands = ol; + $$ = insn; + } + ; + +insn_tkipls : OP_TKIPLS operlist_2 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = $2; + struct operand *flags = xmalloc(sizeof(struct operand)); + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = 0x0 | 0x2; + flags->type = OPER_IMM; + flags->u.imm = imm; + ol->oper[2] = ol->oper[1]; + ol->oper[1] = flags; + insn->op = OP_TKIPH; + insn->operands = ol; + $$ = insn; + } + ; + +insn_nap : OP_NAP { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + struct operlist *ol = xmalloc(sizeof(struct operlist)); + struct operand *regop = xmalloc(sizeof(struct operand)); + struct operand *zeroop = xmalloc(sizeof(struct operand)); + struct registr *r0 = xmalloc(sizeof(struct registr)); + r0->type = GPR; + r0->nr = 0; + regop->type = OPER_REG; + regop->u.reg = r0; + zeroop->type = OPER_RAW; + zeroop->u.raw = 0x000; + ol->oper[0] = regop; + ol->oper[1] = regop; + ol->oper[2] = zeroop; + insn->op = OP_NAP; + insn->operands = ol; + $$ = insn; + } + ; + +insn_raw : raw_code operlist_3 { + struct instruction *insn = xmalloc(sizeof(struct instruction)); + insn->op = RAW_CODE; + insn->operands = $2; + insn->opcode = (unsigned long)$1; + $$ = insn; + } + ; + +raw_code : RAW_CODE { + yytext++; /* skip @ */ + $$ = (void *)(unsigned long)strtoul(yytext, NULL, 16); + } + ; + +extended_operlist : decnum COMMA decnum COMMA operand COMMA operand COMMA operand { + struct operlist *ol = xmalloc(sizeof(struct operlist)); + struct operand *mask_oper = xmalloc(sizeof(struct operand)); + struct operand *shift_oper = xmalloc(sizeof(struct operand)); + mask_oper->type = OPER_RAW; + mask_oper->u.raw = (unsigned long)$1; + shift_oper->type = OPER_RAW; + shift_oper->u.raw = (unsigned long)$3; + ol->oper[0] = mask_oper; + ol->oper[1] = shift_oper; + ol->oper[2] = $5; + ol->oper[3] = $7; + ol->oper[4] = store_oper_sanity($9); + $$ = ol; + } + ; + +external_jump_operands : hexnum COMMA operand COMMA operand COMMA labelref { + struct operlist *ol = xmalloc(sizeof(struct operlist)); + struct operand *cond = xmalloc(sizeof(struct operand)); + cond->type = OPER_RAW; + cond->u.raw = (unsigned long)$1; + ol->oper[0] = cond; + ol->oper[1] = $3; + ol->oper[2] = $5; + ol->oper[3] = $7; + $$ = ol; + } + ; + +operlist_2 : operand COMMA operand { + struct operlist *ol = xmalloc(sizeof(struct operlist)); + ol->oper[0] = $1; + ol->oper[1] = store_oper_sanity($3); + $$ = ol; + } + ; + +operlist_3 : operand COMMA operand COMMA operand { + struct operlist *ol = xmalloc(sizeof(struct operlist)); + ol->oper[0] = $1; + ol->oper[1] = $3; + ol->oper[2] = store_oper_sanity($5); + $$ = ol; + } + ; + +operand : reg { + struct operand *oper = xmalloc(sizeof(struct operand)); + oper->type = OPER_REG; + oper->u.reg = $1; + $$ = oper; + } + | mem { + struct operand *oper = xmalloc(sizeof(struct operand)); + oper->type = OPER_MEM; + oper->u.mem = $1; + $$ = oper; + } + | raw_code { + struct operand *oper = xmalloc(sizeof(struct operand)); + oper->type = OPER_RAW; + oper->u.raw = (unsigned long)$1; + $$ = oper; + } + | imm { + struct operand *oper = xmalloc(sizeof(struct operand)); + oper->type = OPER_IMM; + oper->u.imm = $1; + $$ = oper; + } + | labelref { + $$ = $1; + } + ; + +reg : GPR regnr { + struct registr *reg = xmalloc(sizeof(struct registr)); + reg->type = GPR; + reg->nr = (unsigned long)$2; + $$ = reg; + } + | SPR { + struct registr *reg = xmalloc(sizeof(struct registr)); + reg->type = SPR; + yytext += 3; /* skip "spr" */ + reg->nr = strtoul(yytext, NULL, 16); + $$ = reg; + } + | OFFR regnr { + struct registr *reg = xmalloc(sizeof(struct registr)); + reg->type = OFFR; + reg->nr = (unsigned long)$2; + $$ = reg; + } + ; + +mem : BRACK_OPEN hexnum_decnum BRACK_CLOSE { + struct memory *mem = xmalloc(sizeof(struct memory)); + mem->type = MEM_DIRECT; + mem->offset = (unsigned long)$2; + $$ = mem; + } + | BRACK_OPEN hexnum_decnum COMMA OFFR regnr BRACK_CLOSE { + struct memory *mem = xmalloc(sizeof(struct memory)); + mem->type = MEM_INDIRECT; + mem->offset = (unsigned long)$2; + mem->offr_nr = (unsigned long)$5; + $$ = mem; + } + ; + +imm : hexnum { + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = (unsigned long)$1; + $$ = imm; + } + | decnum { + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = (unsigned long)$1; + $$ = imm; + } + | complex_imm { + struct immediate *imm = xmalloc(sizeof(struct immediate)); + imm->imm = (unsigned long)$1; + $$ = imm; + } + ; + +complex_imm : PAREN_OPEN complex_imm_arg complex_imm_oper complex_imm_arg PAREN_CLOSE { + unsigned long a = (unsigned long)$2; + unsigned long b = (unsigned long)$4; + unsigned long operation = (unsigned long)$3; + unsigned long res = 31337; + switch (operation) { + case PLUS: + res = a + b; + break; + case MINUS: + res = a - b; + break; + case MULTIPLY: + res = a * b; + break; + case DIVIDE: + res = a / b; + break; + case BITW_OR: + res = a | b; + break; + case BITW_AND: + res = a & b; + break; + case BITW_XOR: + res = a ^ b; + break; + case LEFTSHIFT: + res = a << b; + break; + case RIGHTSHIFT: + res = a >> b; + break; + default: + yyerror("Internal parser BUG. complex_imm oper unknown"); + } + $$ = (void *)res; + } + | PAREN_OPEN complex_imm PAREN_CLOSE { + $$ = $2; + } + | PAREN_OPEN BITW_NOT complex_imm PAREN_CLOSE { + unsigned long n = (unsigned long)$3; + n = ~n; + $$ = (void *)n; + } + | PAREN_OPEN complex_imm_const PAREN_CLOSE { + $$ = $2; + } + ; + +complex_imm_oper : PLUS { + $$ = (void *)(unsigned long)PLUS; + } + | MINUS { + $$ = (void *)(unsigned long)MINUS; + } + | MULTIPLY { + $$ = (void *)(unsigned long)MULTIPLY; + } + | DIVIDE { + $$ = (void *)(unsigned long)DIVIDE; + } + | BITW_OR { + $$ = (void *)(unsigned long)BITW_OR; + } + | BITW_AND { + $$ = (void *)(unsigned long)BITW_AND; + } + | BITW_XOR { + $$ = (void *)(unsigned long)BITW_XOR; + } + | LEFTSHIFT { + $$ = (void *)(unsigned long)LEFTSHIFT; + } + | RIGHTSHIFT { + $$ = (void *)(unsigned long)RIGHTSHIFT; + } + ; + +complex_imm_arg : complex_imm_const { + $$ = $1; + } + | complex_imm { + $$ = $1; + } + ; + +complex_imm_const : hexnum_decnum { + $$ = $1; + } + | BITW_NOT hexnum_decnum { + unsigned long n = (unsigned long)$2; + n = ~n; + $$ = (void *)n; + } + ; + +hexnum : HEXNUM { + while (yytext[0] != 'x') { + if (yytext[0] == '\0') + yyerror("Internal HEXNUM parser error"); + yytext++; + } + yytext++; + $$ = (void *)(unsigned long)strtoul(yytext, NULL, 16); + } + ; + +decnum : DECNUM { + $$ = (void *)(unsigned long)strtol(yytext, NULL, 10); + } + ; + +hexnum_decnum : hexnum { + $$ = $1; + } + | decnum { + $$ = $1; + } + ; + +labelref : identifier { + struct operand *oper = xmalloc(sizeof(struct operand)); + struct label *label = xmalloc(sizeof(struct label)); + label->name = $1; + label->direction = LABELREF_ABSOLUTE; + oper->type = OPER_LABEL; + oper->u.label = label; + $$ = oper; + } + | identifier MINUS { + struct operand *oper = xmalloc(sizeof(struct operand)); + struct label *label = xmalloc(sizeof(struct label)); + label->name = $1; + label->direction = LABELREF_RELATIVE_BACK; + oper->type = OPER_LABEL; + oper->u.label = label; + $$ = oper; + } + | identifier PLUS { + struct operand *oper = xmalloc(sizeof(struct operand)); + struct label *label = xmalloc(sizeof(struct label)); + label->name = $1; + label->direction = LABELREF_RELATIVE_FORWARD; + oper->type = OPER_LABEL; + oper->u.label = label; + $$ = oper; + } + ; + +regnr : DECNUM { + $$ = (void *)(unsigned long)strtoul(yytext, NULL, 10); + } + ; + +identifier : IDENT { + $$ = xstrdup(yytext); + } + ; + +%% + +int section = SECTION_TEXT; /* default to .text section */ +struct initvals_sect *cur_initvals_sect; + +void yyerror(char *str) +{ + unsigned int i; + + fprintf(stderr, + "Parser ERROR (file \"%s\", line %u, col %u):\n", + cur_lineinfo.file, + cur_lineinfo.lineno, + cur_lineinfo.column); + fprintf(stderr, "%s\n", cur_lineinfo.linecopy); + for (i = 0; i < cur_lineinfo.column - 1; i++) + fprintf(stderr, " "); + fprintf(stderr, "^\n"); + fprintf(stderr, "%s\n", str); + exit(1); +} + +static struct operand * store_oper_sanity(struct operand *oper) +{ + if (oper->type == OPER_IMM && + oper->u.imm->imm != 0) { + yyerror("Only 0x000 Immediate is allowed for " + "Output operands"); + } + return oper; +} diff --git a/assembler/scanner.l b/assembler/scanner.l new file mode 100644 index 0000000..34559c6 --- /dev/null +++ b/assembler/scanner.l @@ -0,0 +1,226 @@ +%{ + +/* + * Copyright (C) 2006-2007 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "parser.h" +#include "main.h" + +#include + +#undef min +#define min(x,y) ((x) < (y) ? (x) : (y)) + + +static void interpret_cppinfo(const char *); +static void update_lineinfo(void); + +static inline void log_current_line(void) +{ + size_t len; + + len = min(sizeof(cur_lineinfo.linecopy) - 1, strlen(yytext)); + strncpy(cur_lineinfo.linecopy, yytext, len); + cur_lineinfo.linecopy[len] = '\0'; +} + +%} + +IDENTIFIER ([a-zA-Z_][0-9a-zA-Z_]*) +WS ([ \t]) +NEWLINE ((\r)|(\n)|(\r\n)) + +%% + +^.*$ { log_current_line(); REJECT; } +#\ [0-9]+\ \".*\"[ ]*[0-9]*{NEWLINE} { interpret_cppinfo(yytext); } + + +{WS}+ { update_lineinfo(); /* whitespace */ } +{NEWLINE} { cur_lineinfo.lineno++; update_lineinfo(); } +;.*$ { update_lineinfo(); /* comment */ } + +^{WS}*"%"{WS}*arch { update_lineinfo(); return ASM_ARCH; } +^{WS}*"%"{WS}*start { update_lineinfo(); return ASM_START; } + +^{WS}*\.text{WS}*$ { update_lineinfo(); return SECTION_TEXT; } +^{WS}*\.initvals/\({IDENTIFIER}\){WS}*{NEWLINE} { update_lineinfo(); return SECTION_IVALS; } + +spr[0-9a-fA-F]{3,3} { update_lineinfo(); return SPR; } +r/([0-9]|([1-5][0-9])|(6[0-3])) { update_lineinfo(); return GPR; } +off/[0-6] { update_lineinfo(); return OFFR; } +lr/[0-3] { update_lineinfo(); return LR; } + +, { update_lineinfo(); return COMMA; } +\[ { update_lineinfo(); return BRACK_OPEN; } +\] { update_lineinfo(); return BRACK_CLOSE; } +\( { update_lineinfo(); return PAREN_OPEN; } +\) { update_lineinfo(); return PAREN_CLOSE; } + +\+ { update_lineinfo(); return PLUS; } +\- { update_lineinfo(); return MINUS; } +\* { update_lineinfo(); return MULTIPLY; } +\/ { update_lineinfo(); return DIVIDE; } +\| { update_lineinfo(); return BITW_OR; } +\& { update_lineinfo(); return BITW_AND; } +\^ { update_lineinfo(); return BITW_XOR; } +\~ { update_lineinfo(); return BITW_NOT; } +\<\< { update_lineinfo(); return LEFTSHIFT; } +\>\> { update_lineinfo(); return RIGHTSHIFT; } + +add { update_lineinfo(); return OP_ADD; } +add\. { update_lineinfo(); return OP_ADDSC; } +addc { update_lineinfo(); return OP_ADDC; } +addc\. { update_lineinfo(); return OP_ADDSCC; } + +sub { update_lineinfo(); return OP_SUB; } +sub\. { update_lineinfo(); return OP_SUBSC; } +subc { update_lineinfo(); return OP_SUBC; } +subc\. { update_lineinfo(); return OP_SUBSCC; } + +sra { update_lineinfo(); return OP_SRA; } +or { update_lineinfo(); return OP_OR; } +and { update_lineinfo(); return OP_AND; } +xor { update_lineinfo(); return OP_XOR; } +sr { update_lineinfo(); return OP_SR; } +srx { update_lineinfo(); return OP_SRX; } +sl { update_lineinfo(); return OP_SL; } +rl { update_lineinfo(); return OP_RL; } +rr { update_lineinfo(); return OP_RR; } +nand { update_lineinfo(); return OP_NAND; } +orx { update_lineinfo(); return OP_ORX; } +mov { update_lineinfo(); return OP_MOV; } + +jmp { update_lineinfo(); return OP_JMP; } +jand { update_lineinfo(); return OP_JAND; } +jnand { update_lineinfo(); return OP_JNAND; } +js { update_lineinfo(); return OP_JS; } +jns { update_lineinfo(); return OP_JNS; } +je { update_lineinfo(); return OP_JE; } +jne { update_lineinfo(); return OP_JNE; } +jls { update_lineinfo(); return OP_JLS; } +jges { update_lineinfo(); return OP_JGES; } +jgs { update_lineinfo(); return OP_JGS; } +jles { update_lineinfo(); return OP_JLES; } +jl { update_lineinfo(); return OP_JL; } +jge { update_lineinfo(); return OP_JGE; } +jg { update_lineinfo(); return OP_JG; } +jle { update_lineinfo(); return OP_JLE; } +jzx { update_lineinfo(); return OP_JZX; } +jnzx { update_lineinfo(); return OP_JNZX; } +jext { update_lineinfo(); return OP_JEXT; } +jnext { update_lineinfo(); return OP_JNEXT; } + +call { update_lineinfo(); return OP_CALL; } +ret { update_lineinfo(); return OP_RET; } + +tkiph { update_lineinfo(); return OP_TKIPH; } +tkiphs { update_lineinfo(); return OP_TKIPHS; } +tkipl { update_lineinfo(); return OP_TKIPL; } +tkipls { update_lineinfo(); return OP_TKIPLS; } + +nap { update_lineinfo(); return OP_NAP; } + +mmio16 { update_lineinfo(); return IVAL_MMIO16; } +mmio32 { update_lineinfo(); return IVAL_MMIO32; } +phy { update_lineinfo(); return IVAL_PHY; } +radio { update_lineinfo(); return IVAL_RADIO; } +shm16 { update_lineinfo(); return IVAL_SHM16; } +shm32 { update_lineinfo(); return IVAL_SHM32; } + +@[0-9a-fA-F]{3,3} { update_lineinfo(); return RAW_CODE; } + +0x[0-9a-fA-F]+ { update_lineinfo(); return HEXNUM; } +-?[0-9]+ { update_lineinfo(); return DECNUM; } + +bcm43xx_newworld { update_lineinfo(); return ARCH_NEWWORLD; } +bcm43xx_oldworld { update_lineinfo(); return ARCH_OLDWORLD; } + +{IDENTIFIER}: { update_lineinfo(); return LABEL; } +{IDENTIFIER} { update_lineinfo(); return IDENT; } + +%% + +struct lineinfo cur_lineinfo; + +static void interpret_cppinfo(const char *str) +{ + const char * const orig = str; + char tmp[64]; + char *found; + + /* This will interpret lines added by CPP. + * They look like: + * # 3 "file.asm" 1 + */ + + if (*str == '\0') + goto error; + str++; /* skip # character */ + if (*str == '\0') + goto error; + str++; /* skip whitespace */ + + /* Line number */ + found = strchr(str, ' '); + if (!found) + goto error; + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, str, min(sizeof(tmp) - 1, + (int)(found - str))); + cur_lineinfo.lineno = strtoul(tmp, NULL, 10); + str = found; + str++; + + /* File name */ + if (*str != '\"') + goto error; + str++; + if (*str == '\0') + goto error; + found = strchr(str, '\"'); + if (!found) + goto error; + memset(cur_lineinfo.file, 0, sizeof(cur_lineinfo.file)); + memcpy(cur_lineinfo.file, str, + min(sizeof(cur_lineinfo.file) - 1, + (int)(found - str))); + + if (strcmp(cur_lineinfo.file, "") == 0) + strcpy(cur_lineinfo.file, "Input File"); + + return; +error: + fprintf(stderr, "Invalid CPP line directive: %s\n", orig); + exit(1); +} + +static void update_lineinfo(void) +{ + int i = 0; + + while (yytext[i] != '\0') { + switch (yytext[i]) { + case '\r': + case '\n': + cur_lineinfo.column = 0; + break; + case '\t': + cur_lineinfo.column += 8 - (cur_lineinfo.column % 8); + break; + default: + cur_lineinfo.column++; + } + i++; + } +} diff --git a/assembler/test.asm b/assembler/test.asm new file mode 100644 index 0000000..0edadfc --- /dev/null +++ b/assembler/test.asm @@ -0,0 +1,177 @@ +; This is a bcm43xx microcode assembly example. +; +; In this example file, r0 and r1 are always input +; registers and r2 is output. +; For input we can always have constant values or (one) memory +; operand instead of the input registers shown here. +; +; Registers: +; GPRs: r0 - r63 +; Offset Registers: off0 - off5 +; SPRs: spr000 +; +; To access memory, two methods can be used. Examples follow. +; Direct linear: +; mov r0,[0xCA] +; Indirect through Offset Register (pointer): +; mov r0,[0xCA,off0] +; + + +%arch bcm43xx_newworld ; or bcm43xx-oldworld +%start testlabel + +#define PSM_BRC spr848 + +#define ECOND_MAC_ON 0x24 + + +.text + +label: + ; ADD instructions + add r0,r1,r2 ; add + add. r0,r1,r2 ; add, set carry + addc r0,r1,r2 ; add with carry + addc. r0,r1,r2 ; add with carry, set carry + +testlabel: + ; SUB instructions + sub r0,r1,r2 ; sub + sub. r0,r1,r2 ; sub, set carry + subc r0,r1,r2 ; sub with carry + subc. r0,r1,r2 ; sub with carry, set carry + + sra r0,r1,r2 ; arithmetic rightshift + + ; Logical instructions + or r0,r1,r2 ; bitwise OR + and r0,r1,r2 ; bitwise AND + xor r0,r1,r2 ; bitwise XOR + sr r0,r1,r2 ; rightshift + sl r0,r1,r2 ; leftshift + + srx 7,8,r0,r1,r2 ; eXtended right shift (two input regs) + + rl r0,r1,r2 ; rotate left + rr r0,r1,r2 ; rotate right + nand r0,r1,r2 ; clear bits (notmask + and) + + orx 7,8,r0,r1,r2 ; eXtended OR + + ; Copy instruction. This is a virtual instruction + ; translated to more lowlevel stuff like OR. + mov r0,r2 ; copy data + + ; Jumps + jmp label ; unconditional jump + jand r0,r1,label ; jump if binary AND + jnand r0,r1,label ; jump if not binary AND + js r0,r1,label ; jump if all bits set + jns r0,r1,label ; jump if not all bits set + je r0,r1,label ; jump if equal + jne r0,r1,label ; jump if not equal + jls r0,r1,label ; jump if less (signed) + jges r0,r1,label ; jump if greater or equal (signed) + jgs r0,r1,label ; jump if greater (signed) + jles r0,r1,label ; jump if less or equal (signed) + jl r0,r1,label ; jump if less + jge r0,r1,label ; jump if greater or equal + jg r0,r1,label ; jump if greater + jle r0,r1,label ; jump if less or equal + + jzx 7,8,r0,r1,label ; Jump if zero after shift and mask + jnzx 7,8,r0,r1,label ; Jump if nonzero after shift and mask + + ; jump on external conditions + jext ECOND_MAC_ON,r0,r0,label ; jump if external condition is TRUE + jnext ECOND_MAC_ON,r0,r0,label ; jump if external condition is FALSE + + ; Subroutines + call lr0,label ; store PC in lr0, call func at label + ret lr0,lr1 ; store PC in lr0, return to lr1 + ; Both link registers can be the same + ; and don't interfere. + + ; TKIP sbox lookup + tkiph r0,r2 ; Lookup high + tkiphs r0,r2 ; Lookup high, byteswap + tkipl r0,r2 ; Lookup low + tkipls r0,r2 ; Lookup low, byteswap + + nap ; sleep until event + + ; raw instruction + @160 r0,r1,r2 ; equivalent to or r0,r1,r2 + @1C0 @C11, @C22, @BC3 + + + ; Support for directional jumps. + ; Directional jumps can be used to conveniently jump inside of + ; functions without using function specific label prefixes. Note + ; that this does not establish a sub-namespace, though. "loop" + ; and "out" are still in the global namespace and can't be used + ; anymore for absolute jumps (Assembler will warn about duplication). +function_a: + jl r0, r1, out+ + loop: + nap + jmp loop- + out: + ret lr0, lr1 + +function_b: + jl r0, r1, out+ + loop: + nap + jmp loop- + out: + ret lr0, lr1 + + +; The assembler has support for fancy assemble-time +; immediate constant expansion. This is called "complex immediates". +; Complex immediates are _always_ clamped by parentheses. There is no +; operator precedence. You must use parentheses to tell precedence. + mov (2 + 3),r0 + mov (6 - 2),r0 + mov (2 * 3),r0 + mov (10 / 5),r0 + mov (1 | 2),r0 + mov (3 & 2),r0 + mov (3 ^ 2),r0 + mov (~1),r0 + mov (2 << 3),r0 + mov (8 >> 2),r0 + mov (1 << (0x3 + 2)),r0 + mov (1 + (2 + (3 + 4))),r0 + mov (4 >> (((((~5 | 0x21)))) | (~((10) & 2)))),r0 + + +; Some regression testing for the assembler follows + mov 2,off0 ; test memory stuff + xor 0x124,r1,[0x0,off0] ; test memory stuff + xor 0x124,r0,[0x0] ; test memory stuff + mov -34,r0 ; negative dec numbers are supported + or r0,r1,@BC2 ; We also support single raw operands + mov 0xEEEE,r0 ; MOV supports up to 16bit + jand 0x3800,r0,label ; This is emulated by jnzx + jnand 0x3800,r0,label ; This is emulated by jzx + or spr06c,0,spr06c ; Can have one spr input and one spr output + or [0],0,[0] ; Can have one mem input and one mem output + mov testlabel, r0 ; Can use label as immediate value + + +; The .initvals section generates an "Initial Values" file +; with the name "foobar" in this example, which is uploaded +; by the kernel driver on load. This is useful for writing ucode +; specific values to the chip without bloating the small ucode +; memory space with this initialization stuff. +; Values are written in order they appear here. +.initvals(foobar) + mmio16 0x1234, 0xABC ; Write 0x1234 to MMIO register 0xABC + mmio32 0x12345678, 0xABC ; Write 0x12345678 to MMIO register 0xABC + phy 0x1234, 0xABC ; Write 0x1234 to PHY register 0xABC + radio 0x1234, 0xABC ; Write 0x1234 to RADIO register 0xABC + shm16 0x1234, 0x0001, 0x0002 ; Write 0x1234 to SHM routing 0x0001, register 0x0002 + shm32 0x12345678, 0x0001, 0x0002 ; Write 0x12345678 to SHM routing 0x0001, register 0x0002 diff --git a/assembler/util.c b/assembler/util.c new file mode 100644 index 0000000..2f4f7a0 --- /dev/null +++ b/assembler/util.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "util.h" + +#include +#include + + +void dump(const char *data, + size_t size, + const char *description) +{ + size_t i; + char c; + + fprintf(stderr, "Data dump (%s, %zd bytes):", + description, size); + for (i = 0; i < size; i++) { + c = data[i]; + if (i % 8 == 0) { + fprintf(stderr, "\n0x%08zx: 0x%02x, ", + i, c & 0xff); + } else + fprintf(stderr, "0x%02x, ", c & 0xff); + } + fprintf(stderr, "\n"); +} + +void * xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + memset(p, 0, size); + + return p; +} + +char * xstrdup(const char *str) +{ + char *c; + + c = strdup(str); + if (!c) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + + return c; +} + +be16_t cpu_to_be16(uint16_t x) +{ + be16_t ret; + uint8_t *tmp = (uint8_t *)(&ret); + + tmp[0] = (x & 0xFF00) >> 8; + tmp[1] = (x & 0x00FF); + + return ret; +} + +be32_t cpu_to_be32(uint32_t x) +{ + be32_t ret; + uint8_t *tmp = (uint8_t *)(&ret); + + tmp[0] = (x & 0xFF000000) >> 24; + tmp[1] = (x & 0x00FF0000) >> 16; + tmp[2] = (x & 0x0000FF00) >> 8; + tmp[3] = (x & 0x000000FF); + + return ret; +} diff --git a/assembler/util.h b/assembler/util.h new file mode 100644 index 0000000..a3d1a82 --- /dev/null +++ b/assembler/util.h @@ -0,0 +1,28 @@ +#ifndef BCM43xx_ASM_UTIL_H_ +#define BCM43xx_ASM_UTIL_H_ + +#include +#include + + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + + +void dump(const char *data, + size_t size, + const char *description); + + +void * xmalloc(size_t size); +char * xstrdup(const char *str); + + +typedef _Bool bool; + +typedef uint16_t be16_t; +typedef uint32_t be32_t; + +be16_t cpu_to_be16(uint16_t x); +be32_t cpu_to_be32(uint32_t x); + +#endif /* BCM43xx_ASM_UTIL_H_ */ diff --git a/disassembler/COPYING b/disassembler/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/disassembler/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/disassembler/Makefile b/disassembler/Makefile new file mode 100644 index 0000000..c58e3f9 --- /dev/null +++ b/disassembler/Makefile @@ -0,0 +1,26 @@ +CC = gcc +PREFIX = /usr/local +CFLAGS = -std=gnu99 -O2 -fomit-frame-pointer -Wall -D_BSD_SOURCE -D_GNU_SOURCE +LDFLAGS = + +BINARY = bcm43xx-dasm.bin +OBJECTS = main.o util.o + +all: $(BINARY) + +main.o: util.h list.h + +util.o: util.h + +$(BINARY): $(OBJECTS) + $(CC) $(CFLAGS) -o $(BINARY) $(OBJECTS) $(LDFLAGS) + +install: all + -install -o 0 -g 0 -m 755 $(BINARY) $(PREFIX)/bin/ + -cp bcm43xx-dasm bcm43xx-dasm.inst + -sed -i -e 's/installed=0/installed=1/' bcm43xx-dasm.inst + -install -o 0 -g 0 -m 755 bcm43xx-dasm.inst $(PREFIX)/bin/bcm43xx-dasm + -rm -f bcm43xx-dasm.inst + +clean: + -rm -f *~ *.o *.orig *.rej $(BINARY) diff --git a/disassembler/bcm43xx-dasm b/disassembler/bcm43xx-dasm new file mode 100755 index 0000000..aad3321 --- /dev/null +++ b/disassembler/bcm43xx-dasm @@ -0,0 +1,22 @@ +#!/bin/sh + +installed=0 + +if [ $# -ne 2 ]; then + echo "Usage: $0 input_file.fw output_file.asm" + exit 1 +fi + +if [ -z "$BCM43xx_DASM" ]; then + if [ $installed -eq 0 ] && [ -x "./bcm43xx-dasm.bin" ]; then + BCM43xx_DASM="./bcm43xx-dasm.bin" + else + BCM43xx_DASM="bcm43xx-dasm.bin" + fi +fi + +infile="$1" +outfile="$2" + +cat "$infile" | $BCM43xx_DASM "$outfile" + diff --git a/disassembler/list.h b/disassembler/list.h new file mode 100644 index 0000000..ce26357 --- /dev/null +++ b/disassembler/list.h @@ -0,0 +1,296 @@ +/* + * Copied from the Linux kernel source tree, version 2.6.0-test1. + * + * Licensed under the GPL v2 as per the whole kernel source tree. + * + */ + +#ifndef _LIST_H +#define _LIST_H + +#ifndef __GNUC__ +# error "Need GNU GCC" +#endif + +#define typeof __typeof__ +#define offsetof(type, member) __builtin_offsetof (type, member) + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H */ diff --git a/disassembler/main.c b/disassembler/main.c new file mode 100644 index 0000000..64e9975 --- /dev/null +++ b/disassembler/main.c @@ -0,0 +1,774 @@ +/* + * Copyright (C) 2006 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "list.h" +#include "util.h" + +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +/* The header that fwcutter puts in to every .fw file */ +struct fw_header { + /* Type of the firmware data */ + uint8_t type; + /* Version number of the firmware data format */ + uint8_t ver; + uint8_t __padding[2]; + /* Size of the data. For ucode and PCM this is in bytes. + * For IV this is in number-of-ivs. */ + uint32_t size; +} __attribute__ ((__packed__)); + + +struct bin_instruction { + unsigned int opcode; + unsigned int operands[3]; +}; + +struct statement { + enum { + STMT_INSN, + STMT_LABEL, + } type; + + union { + struct { + struct bin_instruction *bin; + const char *name; + const char *operands[5]; + + int is_labelref; + unsigned int labeladdr; + struct statement *labelref; + } insn; + struct { + char *name; + } label; + } u; + + struct list_head list; +}; + +struct disassembler_context { + struct bin_instruction *code; + size_t nr_insns; + + struct list_head stmt_list; +}; + + +static FILE *infile; +const char *outfile_name; + + +static const char * gen_raw_code(unsigned int operand) +{ + char *ret; + + ret = xmalloc(5); + snprintf(ret, 5, "@%03X", operand); + + return ret; +} + +static const char * disasm_mem_operand(unsigned int operand) +{ + char *ret; + + ret = xmalloc(8); + snprintf(ret, 8, "[0x%03X]", operand); + + return ret; +} + +static const char * disasm_indirect_mem_operand(unsigned int operand) +{ + char *ret; + + ret = xmalloc(12); + snprintf(ret, 12, "[0x%02X,off%u]", + (operand & 0x3F), ((operand >> 6) & 0x7)); + + return ret; +} + +static const char * disasm_imm_operand(unsigned int operand) +{ + char *ret; + + operand &= ~0xC00; + + ret = xmalloc(7); + if (operand & (1 << 9)) + snprintf(ret, 7, "0x%04X", (operand | 0xFC00)); + else + snprintf(ret, 7, "0x%03X", operand); + + return ret; +} + +static const char * disasm_spr_operand(unsigned int operand) +{ + char *ret; + + ret = xmalloc(7); + snprintf(ret, 7, "spr%03X", (operand & 0x1FF)); + + return ret; +} + +static const char * disasm_gpr_operand(unsigned int operand) +{ + char *ret; + + ret = xmalloc(4); + snprintf(ret, 4, "r%u", (operand & 0x3F)); + + return ret; +} + +static const char * disasm_offr_operand(unsigned int operand) +{ + char *ret; + + ret = xmalloc(5); + snprintf(ret, 5, "off%u", (operand & 0x7)); + + return ret; +} + +static void disasm_std_operand(struct statement *stmt, + int oper_idx, + int out_idx, + int forceraw) +{ + unsigned int operand = stmt->u.insn.bin->operands[oper_idx]; + + if (forceraw) + goto raw; + + if (!(operand & 0x800)) { + stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand); + return; + } else if ((operand & 0xC00) == 0xC00) { + stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand); + return; + } else if ((operand & 0xFC0) == 0xBC0) { + stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand); + return; + } else if ((operand & 0xE00) == 0x800) { + stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand); + return; + } else if ((operand & 0xFF8) == 0x860) { + stmt->u.insn.operands[out_idx] = disasm_offr_operand(operand); + return; + } else if ((operand & 0xE00) == 0xA00) { + stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand); + return; + } +raw: + stmt->u.insn.operands[out_idx] = gen_raw_code(operand); +} + +static void disasm_constant_opcodes(struct disassembler_context *ctx, + struct statement *stmt) +{ + struct bin_instruction *bin = stmt->u.insn.bin; + + switch (bin->opcode) { + case 0x1C0: + stmt->u.insn.name = "add"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1C2: + stmt->u.insn.name = "add."; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1C1: + stmt->u.insn.name = "addc"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1C3: + stmt->u.insn.name = "addc."; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1D0: + stmt->u.insn.name = "sub"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1D2: + stmt->u.insn.name = "sub."; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1D1: + stmt->u.insn.name = "subc"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1D3: + stmt->u.insn.name = "subc."; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x130: + stmt->u.insn.name = "sra"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x160: + stmt->u.insn.name = "or"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x140: + stmt->u.insn.name = "and"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x170: + stmt->u.insn.name = "xor"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x120: + stmt->u.insn.name = "sr"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x110: + stmt->u.insn.name = "sl"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1A0: + stmt->u.insn.name = "rl"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x1B0: + stmt->u.insn.name = "rr"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x150: + stmt->u.insn.name = "nand"; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + case 0x040: + stmt->u.insn.name = "jand"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x040 | 0x1): + stmt->u.insn.name = "jnand"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x050: + stmt->u.insn.name = "js"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x050 | 0x1): + stmt->u.insn.name = "jns"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x0D0: + stmt->u.insn.name = "je"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x0D0 | 0x1): + stmt->u.insn.name = "jne"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x0D2: + stmt->u.insn.name = "jls"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x0D2 | 0x1): + stmt->u.insn.name = "jges"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x0D4: + stmt->u.insn.name = "jgs"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x0D4 | 0x1): + stmt->u.insn.name = "jles"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x0DA: + stmt->u.insn.name = "jl"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x0DA | 0x1): + stmt->u.insn.name = "jge"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x0DC: + stmt->u.insn.name = "jg"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case (0x0DC | 0x1): + stmt->u.insn.name = "jle"; + stmt->u.insn.is_labelref = 2; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 1, 1, 0); + break; + case 0x002: { + char *str; + + stmt->u.insn.name = "call"; + stmt->u.insn.is_labelref = 1; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + str = xmalloc(4); + snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]); + stmt->u.insn.operands[0] = str; + break; + } + case 0x003: { + char *str; + + stmt->u.insn.name = "ret"; + str = xmalloc(4); + snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]); + stmt->u.insn.operands[0] = str; + str = xmalloc(4); + snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[2]); + stmt->u.insn.operands[2] = str; + break; + } + case 0x1E0: { + unsigned int flags; + + flags = stmt->u.insn.bin->operands[1]; + switch (flags & ~0xC00) { + case 0x1: + stmt->u.insn.name = "tkiph"; + break; + case (0x1 | 0x2): + stmt->u.insn.name = "tkiphs"; + break; + case 0x0: + stmt->u.insn.name = "tkipl"; + break; + case (0x0 | 0x2): + stmt->u.insn.name = "tkipls"; + break; + default: + fprintf(stderr, "Invalid TKIP flags %X\n", + flags); + exit(1); + } + disasm_std_operand(stmt, 0, 0, 0); + disasm_std_operand(stmt, 2, 2, 0); + break; + } + case 0x001: { + stmt->u.insn.name = "nap"; + if (stmt->u.insn.bin->operands[0] != 0xBC0) { + fprintf(stderr, "NAP: invalid first argument 0x%03X\n", + stmt->u.insn.bin->operands[0]); + } + if (stmt->u.insn.bin->operands[1] != 0xBC0) { + fprintf(stderr, "NAP: invalid second argument 0x%03X\n", + stmt->u.insn.bin->operands[1]); + } + if (stmt->u.insn.bin->operands[2] != 0x000) { + fprintf(stderr, "NAP: invalid third argument 0x%03X\n", + stmt->u.insn.bin->operands[2]); + } + break; + } + default: + stmt->u.insn.name = gen_raw_code(bin->opcode); + disasm_std_operand(stmt, 0, 0, 1); + disasm_std_operand(stmt, 1, 1, 1); + disasm_std_operand(stmt, 2, 2, 1); + break; + } +} + +static void disasm_opcodes(struct disassembler_context *ctx) +{ + struct bin_instruction *bin; + size_t i; + struct statement *stmt; + char *str; + + for (i = 0; i < ctx->nr_insns; i++) { + bin = &(ctx->code[i]); + + stmt = xmalloc(sizeof(struct statement)); + stmt->type = STMT_INSN; + INIT_LIST_HEAD(&stmt->list); + stmt->u.insn.bin = bin; + stmt->u.insn.is_labelref = -1; + + switch (bin->opcode & 0xF00) { + case 0x200: + stmt->u.insn.name = "srx"; + + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4); + stmt->u.insn.operands[0] = str; + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x00F)); + stmt->u.insn.operands[1] = str; + + disasm_std_operand(stmt, 0, 2, 0); + disasm_std_operand(stmt, 1, 3, 0); + disasm_std_operand(stmt, 2, 4, 0); + break; + case 0x300: + stmt->u.insn.name = "orx"; + + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4); + stmt->u.insn.operands[0] = str; + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x00F)); + stmt->u.insn.operands[1] = str; + + disasm_std_operand(stmt, 0, 2, 0); + disasm_std_operand(stmt, 1, 3, 0); + disasm_std_operand(stmt, 2, 4, 0); + break; + case 0x400: + stmt->u.insn.name = "jzx"; + + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4); + stmt->u.insn.operands[0] = str; + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x00F)); + stmt->u.insn.operands[1] = str; + + disasm_std_operand(stmt, 0, 2, 0); + disasm_std_operand(stmt, 1, 3, 0); + stmt->u.insn.is_labelref = 4; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + break; + case 0x500: + stmt->u.insn.name = "jnzx"; + + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4); + stmt->u.insn.operands[0] = str; + str = xmalloc(3); + snprintf(str, 3, "%d", (bin->opcode & 0x00F)); + stmt->u.insn.operands[1] = str; + + disasm_std_operand(stmt, 0, 2, 0); + disasm_std_operand(stmt, 1, 3, 0); + stmt->u.insn.is_labelref = 4; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + break; + case 0x600: + stmt->u.insn.name = "jnext"; + + str = xmalloc(5); + snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF)); + stmt->u.insn.operands[0] = str; + + disasm_std_operand(stmt, 0, 1, 0); + disasm_std_operand(stmt, 1, 2, 0); + stmt->u.insn.is_labelref = 3; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + break; + case 0x700: + stmt->u.insn.name = "jext"; + + str = xmalloc(5); + snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF)); + stmt->u.insn.operands[0] = str; + + disasm_std_operand(stmt, 0, 1, 0); + disasm_std_operand(stmt, 1, 2, 0); + stmt->u.insn.is_labelref = 3; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + break; + default: + disasm_constant_opcodes(ctx, stmt); + break; + } + + list_add_tail(&stmt->list, &ctx->stmt_list); + } +} + +static struct statement * get_label_at(struct disassembler_context *ctx, + unsigned int addr) +{ + unsigned int addrcnt = 0; + struct statement *stmt, *ret, *prev; + + list_for_each_entry(stmt, &ctx->stmt_list, list) { + if (stmt->type != STMT_INSN) + continue; + if (addrcnt == addr) { + prev = list_entry(stmt->list.prev, struct statement, list); + if (prev->type == STMT_LABEL) + return prev; + ret = xmalloc(sizeof(struct statement)); + INIT_LIST_HEAD(&ret->list); + ret->type = STMT_LABEL; + list_add(&ret->list, &prev->list); + + return ret; + } + addrcnt++; + } + + return NULL; +} + +static void resolve_labels(struct disassembler_context *ctx) +{ + struct statement *stmt; + struct statement *label; + struct statement *n; + unsigned int labeladdr; + unsigned int namecnt = 0; + + /* Resolve label references */ + list_for_each_entry_safe(stmt, n, &ctx->stmt_list, list) { + if (stmt->type != STMT_INSN) + continue; + if (stmt->u.insn.is_labelref == -1) + continue; + labeladdr = stmt->u.insn.labeladdr; + label = get_label_at(ctx, labeladdr); + if (!label) { + fprintf(stderr, "Labeladdress %X out of bounds\n", + labeladdr); + exit(1); + } + stmt->u.insn.labelref = label; + } + + /* Name the labels */ + list_for_each_entry(stmt, &ctx->stmt_list, list) { + if (stmt->type != STMT_LABEL) + continue; + stmt->u.label.name = xmalloc(20); + snprintf(stmt->u.label.name, 20, "L%u", namecnt); + namecnt++; + } +} + +static void emit_asm(struct disassembler_context *ctx) +{ + struct statement *stmt; + FILE *fd; + int first, i; + + fd = fopen(outfile_name, "w+"); + if (!fd) { + fprintf(stderr, "Could not open output file \"%s\"\n", + outfile_name); + exit(1); + } + fprintf(fd, "%%arch bcm43xx_newworld\n\n"); + list_for_each_entry(stmt, &ctx->stmt_list, list) { + switch (stmt->type) { + case STMT_INSN: + fprintf(fd, "\t%s", stmt->u.insn.name); + first = 1; + for (i = 0; i < ARRAY_SIZE(stmt->u.insn.operands); i++) { + if (stmt->u.insn.is_labelref == i) { + fprintf(fd, ",%s", + stmt->u.insn.labelref->u.label.name); + } + if (!stmt->u.insn.operands[i]) + continue; + if (first) + fprintf(fd, "\t"); + if (!first) + fprintf(fd, ","); + first = 0; + fprintf(fd, "%s", + stmt->u.insn.operands[i]); + } + fprintf(fd, "\n"); + break; + case STMT_LABEL: + fprintf(fd, "%s:\n", stmt->u.label.name); + break; + } + } + fclose(fd); +} + +static void read_input(struct disassembler_context *ctx) +{ + size_t size = 0, pos = 0; + size_t ret; + struct bin_instruction *code = NULL; + unsigned char tmp[sizeof(uint64_t)]; + uint64_t codeword; + struct fw_header hdr; + + ret = fread(&hdr, 1, sizeof(hdr), infile); + if (!ret || ret != sizeof(hdr)) { + fprintf(stderr, "Corrupt input file (not fwcutter output)\n"); + exit(1); + } + + if (hdr.ver != 1) { + fprintf(stderr, "Invalid fwcutter header version\n"); + exit(1); + } + + if (hdr.type != 'u') { + fprintf(stderr, "Not a microcode image\n"); + exit(1); + } + + while (1) { + if (pos >= size) { + size += 512; + code = xrealloc(code, size * sizeof(struct bin_instruction)); + } + ret = fread(tmp, 1, sizeof(uint64_t), infile); + if (!ret) + break; + if (ret != sizeof(uint64_t)) { + fprintf(stderr, "Corrupt input file (not 8 byte aligned)\n"); + exit(1); + } + + codeword = 0; + codeword |= ((uint64_t)tmp[0]) << 56; + codeword |= ((uint64_t)tmp[1]) << 48; + codeword |= ((uint64_t)tmp[2]) << 40; + codeword |= ((uint64_t)tmp[3]) << 32; + codeword |= ((uint64_t)tmp[4]) << 24; + codeword |= ((uint64_t)tmp[5]) << 16; + codeword |= ((uint64_t)tmp[6]) << 8; + codeword |= ((uint64_t)tmp[7]); + + code[pos].opcode = (codeword >> 4) & 0xFFF; + code[pos].operands[0] = (codeword & 0xF) << 8; + code[pos].operands[0] |= (codeword >> 56) & 0xFF; + code[pos].operands[1] = (codeword >> 44) & 0xFFF; + code[pos].operands[2] = (codeword >> 32) & 0xFFF; + + pos++; + } + + ctx->code = code; + ctx->nr_insns = pos; +} + +static void disassemble(void) +{ + struct disassembler_context ctx; + + memset(&ctx, 0, sizeof(ctx)); + INIT_LIST_HEAD(&ctx.stmt_list); + + read_input(&ctx); + disasm_opcodes(&ctx); + resolve_labels(&ctx); + emit_asm(&ctx); +} + +static void usage(int argc, char **argv) +{ + fprintf(stderr, "Usage: %s output_file\n", argv[0]); +} + +static void parse_args(int argc, char **argv) +{ + if (argc != 2) { + usage(argc, argv); + exit(1); + } + outfile_name = argv[1]; +} + +int main(int argc, char **argv) +{ + infile = stdin; + parse_args(argc, argv); + disassemble(); + + /* Lazyman simply leaks all allocated memory. */ + return 0; +} diff --git a/disassembler/util.c b/disassembler/util.c new file mode 100644 index 0000000..1854faf --- /dev/null +++ b/disassembler/util.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2006 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "util.h" + +#include +#include + + +void dump(const char *data, + size_t size, + const char *description) +{ + size_t i; + char c; + + fprintf(stderr, "Data dump (%s, %zd bytes):", + description, size); + for (i = 0; i < size; i++) { + c = data[i]; + if (i % 8 == 0) { + fprintf(stderr, "\n0x%08zx: 0x%02x, ", + i, c & 0xff); + } else + fprintf(stderr, "0x%02x, ", c & 0xff); + } + fprintf(stderr, "\n"); +} + +void * xmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + memset(p, 0, size); + + return p; +} + +char * xstrdup(const char *str) +{ + char *c; + + c = strdup(str); + if (!c) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + + return c; +} + +void * xrealloc(void *in, size_t size) +{ + void *out; + + out = realloc(in, size); + if (!out) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + + return out; +} diff --git a/disassembler/util.h b/disassembler/util.h new file mode 100644 index 0000000..843d347 --- /dev/null +++ b/disassembler/util.h @@ -0,0 +1,16 @@ +#ifndef BCM43xx_DASM_UTIL_H_ +#define BCM43xx_DASM_UTIL_H_ + +#include + + +void dump(const char *data, + size_t size, + const char *description); + + +void * xmalloc(size_t size); +char * xstrdup(const char *str); +void * xrealloc(void *in, size_t size); + +#endif /* BCM43xx_DASM_UTIL_H_ */ diff --git a/fwcutter/Makefile b/fwcutter/Makefile new file mode 100644 index 0000000..2ddd8bd --- /dev/null +++ b/fwcutter/Makefile @@ -0,0 +1,34 @@ +VERSION = 008 + +CC ?= cc +PREFIX ?= /usr/local +CFLAGS ?= -Os -fomit-frame-pointer +CFLAGS += -std=c99 -Wall -pedantic -D_BSD_SOURCE +LDFLAGS ?= + + +OBJECTS = fwcutter.o md5.o +BIN = b43-fwcutter + +CFLAGS += -DFWCUTTER_VERSION_=$(VERSION) + +all: $(BIN) + +fwcutter.o: fwcutter.h fwcutter_list.h md5.h + +md5.o: md5.h + +$(BIN): $(OBJECTS) + $(CC) $(CFLAGS) -o $(BIN) $(OBJECTS) $(LDFLAGS) + +install: all + -install -d -o 0 -g 0 -m 755 $(PREFIX)/bin/ + -install -o 0 -g 0 -m 755 $(BIN) $(PREFIX)/bin/ + -install -d -o 0 -g 0 -m 755 $(PREFIX)/man/man1/ + -install -o 0 -g 0 -m 644 $(BIN).1 $(PREFIX)/man/man1/ + +clean: + -rm -f *.o $(BIN) + +distclean: clean + -rm -f *.fw *.orig *.rej *~ diff --git a/fwcutter/README b/fwcutter/README new file mode 100644 index 0000000..91e63e5 --- /dev/null +++ b/fwcutter/README @@ -0,0 +1,47 @@ + BCM43XX Linux Driver Project + ============================ + + +About this software +------------------- + +b43-fwcutter is a tool which can extract firmware from various source files. +It's written for BCM43xx driver files. + +The project page is http://bcm43xx.berlios.de/ + + +Usage +----- + +b43-fwcutter FILE extracts the firmware from a source FILE to + the current directory, creating a directory + "b43" or "b43legacy" with files in it. +b43-fwcutter -i FILE identify the driver file; print information + but don't extract. +b43-fwcutter -w /tmp FILE extract and write firmware to /tmp. +b43-fwcutter -l prints a list of supported driver source files. + +After extraction, copy the "b43" or "b43legacy" directory to your firmware +directory (usually /lib/firmware or similar, see below). Alternatively you +can use the -w option to b43-fwcutter. + +Different distributions use different directories to load firmware from. +If the driver complains about missing firmware files look for the correct +directory. For some distributions you have to use /lib/hotplug/firmware +or /usr/lib/hotplug/firmware but other directories are also possible. + +Because firmware file are stored in big endian, extraction doesn't depend +on the system you're using. You can use extracted firmware from any driver +on any system and also copy the extracted files to any other system (e.g. +if extraction on the target system isn't feasible.) + + +Where can I find some driver source files? +------------------------------------------ + +Please check the references at +http://www.linuxwireless.org/en/users/Drivers/bcm43xx#devicefirmware + +It is recommended that you extract firmware for both b43 and b43legacy in +order to support both types of cards on your system. diff --git a/fwcutter/b43-fwcutter.1 b/fwcutter/b43-fwcutter.1 new file mode 100644 index 0000000..e55afe2 --- /dev/null +++ b/fwcutter/b43-fwcutter.1 @@ -0,0 +1,41 @@ +.\" Initially generated by help2man 1.36. +.TH B43-FWCUTTER "1" "2007" "b43-fwcutter" "User Commands" +.SH NAME +b43-fwcutter \- manual page for b43-fwcutter +.SH SYNOPSIS +.B b43-fwcutter +[\fIOPTION\fR] [\fIDRIVER\fR] +.SH DESCRIPTION +b43-fwcutter can extract the firmware for your Broadcom 43xx hardware from different closed source drivers. The b43 driver depends on this firmware files and can't work without them. +.PP +Currently b43-fwcutter supports Apple MacOS X, Microsoft Windows 98/ME/2000/XP and Linux drivers, but keep in mind that b43-fwcutter doesn't support all driver versions. +.PP +Example: +.IP +b43-fwcutter bcmwl5.sys +.PP +to cut the firmware out of bcmwl5.sys +.SH OPTIONS +.TP +\fB\-l\fR|\-\-list +List supported driver versions +.TP +\fB\-i\fR|\-\-identify +Only identify the driver file (don't extract) +.TP +\fB\-w\fR|\-\-target\-dir DIR +Extract and write firmware to DIR +.TP +\fB\-a\fR|\-\-alt\-iv +Extract alternative initvals (only 3.10.x.x) +.TP +\fB\-p\fR|\-\-postfix ".FOO" +Postfix for firmware filenames (.FOO.fw) +.TP +\fB\-v\fR|\-\-version +Print b43-fwcutter version +.TP +\fB\-h\fR|\-\-help +Print help information +.SH AUTHORS +b43 development team, http://bcm43xx.berlios.de diff --git a/fwcutter/fwcutter.c b/fwcutter/fwcutter.c new file mode 100644 index 0000000..fef9099 --- /dev/null +++ b/fwcutter/fwcutter.c @@ -0,0 +1,678 @@ +/* + * firmware cutter for broadcom 43xx wireless driver files + * + * Copyright (c) 2005 Martin Langer , + * 2005-2007 Michael Buesch + * 2005 Alex Beregszaszi + * 2007 Johannes Berg + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __DragonFly__ +#include +#else +#include +#endif + +#include "md5.h" +#include "fwcutter.h" +#include "fwcutter_list.h" + +#ifdef __DragonFly__ +#define V3_FW_DIRNAME "v3" +#define V4_FW_DIRNAME "v4" +#else +#define V3_FW_DIRNAME "b43legacy" +#define V4_FW_DIRNAME "b43" +#endif + +static struct cmdline_args cmdargs; + + +/* Convert a CPU-endian 16bit integer to Big-Endian */ +static be16_t to_be16(uint16_t v) +{ + uint8_t ret[2]; + + ret[0] = (v & 0xFF00) >> 8; + ret[1] = (v & 0x00FF); + + return *((be16_t *)ret); +} + +#if 0 +/* Convert a Big-Endian 16bit integer to CPU-endian */ +static uint16_t from_be16(be16_t v) +{ + uint16_t ret = 0; + + ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8; + ret |= (uint16_t)(((uint8_t *)&v)[1]); + + return ret; +} +#endif + +/* Convert a CPU-endian 32bit integer to Big-Endian */ +static be32_t to_be32(uint32_t v) +{ + uint8_t ret[4]; + + ret[0] = (v & 0xFF000000) >> 24; + ret[1] = (v & 0x00FF0000) >> 16; + ret[2] = (v & 0x0000FF00) >> 8; + ret[3] = (v & 0x000000FF); + + return *((be32_t *)ret); +} + +/* Convert a Big-Endian 32bit integer to CPU-endian */ +static uint32_t from_be32(be32_t v) +{ + uint32_t ret = 0; + + ret |= (uint32_t)(((uint8_t *)&v)[0]) << 24; + ret |= (uint32_t)(((uint8_t *)&v)[1]) << 16; + ret |= (uint32_t)(((uint8_t *)&v)[2]) << 8; + ret |= (uint32_t)(((uint8_t *)&v)[3]); + + return ret; +} + +/* tiny disassembler */ +static void print_ucode_version(struct insn *insn) +{ + int val; + + /* + * The instruction we're looking for is a store to memory + * offset insn->op3 of the constant formed like `val' below. + * 0x2de00 is the opcode for type 1, 0x378 is the opcode + * for type 2 and 3. + */ + if (insn->opcode != 0x378 && insn->opcode != 0x2de00) + return; + + val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2); + + /* + * Memory offsets are word-offsets, for the meaning + * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory + */ + switch (insn->op3) { + case 0: + printf(" ucode version: %d\n", val); + break; + case 1: + printf(" ucode revision: %d\n", val); + break; + case 2: + printf(" ucode date: %.4d-%.2d-%.2d\n", + 2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF); + break; + case 3: + printf(" ucode time: %.2d:%.2d:%.2d\n", + val >> 11, (val >> 5) & 0x3f, val & 0x1f); + break; + } +} + +static void disasm_ucode_1(uint64_t in, struct insn *out) +{ + /* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz + * if we swap the upper and lower 32-bits first it becomes easier: + * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz + */ + in = (in >> 32) | (in << 32); + + out->op3 = in & 0xFFF; + out->op2 = (in >> 12) & 0xFFF; + out->op1 = (in >> 24) & 0xFFF; + out->opcode = (in >> 36) & 0xFFFFF; + /* the rest of the in word should be zero */ +} + +static void disasm_ucode_2(uint64_t in, struct insn *out) +{ + /* xxyyyzzz0000oooX -> ooo Xxx yyy zzz + * if we swap the upper and lower 32-bits first it becomes easier: + * 0000oooxxxyyyzzz -> ooo xxx yyy zzz + */ + in = (in >> 32) | (in << 32); + + out->op3 = in & 0xFFF; + out->op2 = (in >> 12) & 0xFFF; + out->op1 = (in >> 24) & 0xFFF; + out->opcode = (in >> 36) & 0xFFF; + /* the rest of the in word should be zero */ +} + +static void disasm_ucode_3(uint64_t in, struct insn *out) +{ + /* + * like 2, but each operand has one bit more; appears + * to use the same instruction set slightly extended + */ + in = (in >> 32) | (in << 32); + + out->op3 = in & 0x1FFF; + out->op2 = (in >> 13) & 0x1FFF; + out->op1 = (in >> 26) & 0x1FFF; + out->opcode = (in >> 39) & 0xFFF; + /* the rest of the in word should be zero */ +} + +static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len) +{ + uint64_t *insns = (uint64_t*)data; + struct insn insn; + uint32_t i; + + for (i=0; ireg = bswap_16(iv->reg); + iv->size = bswap_16(iv->size); + iv->val = bswap_32(iv->val); +} + +static void build_ivs(struct b43_iv **_out, size_t *_out_size, + struct iv *in, size_t in_size, + struct fw_header *hdr, + uint32_t flags) +{ + struct iv *iv; + struct b43_iv *out; + uint32_t i; + size_t out_size = 0; + + if (sizeof(struct b43_iv) != 6) { + printf("sizeof(struct b43_iv) != 6\n"); + exit(255); + } + + out = malloc(in_size); + if (!out) { + perror("failed to allocate buffer"); + exit(1); + } + *_out = out; + for (i = 0; i < in_size / sizeof(*iv); i++, in++) { + if (flags & FW_FLAG_LE) + swap_endianness_iv(in); + /* input-IV is BigEndian */ + if (in->reg & to_be16(~FW_IV_OFFSET_MASK)) { + printf("Input file IV offset > 0x%X\n", FW_IV_OFFSET_MASK); + exit(1); + } + out->offset_size = in->reg; + if (in->size == to_be16(4)) { + out->offset_size |= to_be16(FW_IV_32BIT); + out->data.d32 = in->val; + + out_size += sizeof(be16_t) + sizeof(be32_t); + out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be32_t)); + } else if (in->size == to_be16(2)) { + if (in->val & to_be32(~0xFFFF)) { + printf("Input file 16bit IV value overflow\n"); + exit(1); + } + out->data.d16 = to_be16(from_be32(in->val)); + + out_size += sizeof(be16_t) + sizeof(be16_t); + out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t)); + } else { + printf("Input file IV size != 2|4\n"); + exit(1); + } + } + hdr->size = to_be32(i); + *_out_size = out_size; +} + +static void write_file(const char *name, uint8_t *buf, uint32_t len, + const struct fw_header *hdr, uint32_t flags) +{ + FILE *f; + char nbuf[4096]; + const char *dir; + int r; + + if (flags & FW_FLAG_V4) + dir = V4_FW_DIRNAME; + else + dir = V3_FW_DIRNAME; + + r = snprintf(nbuf, sizeof(nbuf), + "%s/%s", cmdargs.target_dir, dir); + if (r >= sizeof(nbuf)) { + fprintf(stderr, "name too long"); + exit(2); + } + + r = mkdir(nbuf, 0770); + if (r && errno != EEXIST) { + perror("failed to create output directory"); + exit(2); + } + + r = snprintf(nbuf, sizeof(nbuf), + "%s/%s/%s.fw", cmdargs.target_dir, dir, name); + if (r >= sizeof(nbuf)) { + fprintf(stderr, "name too long"); + exit(2); + } + f = fopen(nbuf, "w"); + if (!f) { + perror("failed to open file"); + exit(2); + } + if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) { + perror("failed to write file"); + exit(2); + } + if (fwrite(buf, 1, len, f) != len) { + perror("failed to write file"); + exit(2); + } + fclose(f); +} + +static void extract_or_identify(FILE *f, const struct extract *extract, + uint32_t flags) +{ + uint8_t *buf; + size_t data_length; + int ucode_rev = 0; + struct fw_header hdr; + + memset(&hdr, 0, sizeof(hdr)); + hdr.ver = FW_HDR_VER; + + if (fseek(f, extract->offset, SEEK_SET)) { + perror("failed to seek on file"); + exit(2); + } + + buf = malloc(extract->length); + if (!buf) { + perror("failed to allocate buffer"); + exit(3); + } + if (fread(buf, 1, extract->length, f) != extract->length) { + perror("failed to read complete data"); + exit(3); + } + + switch (extract->type) { + case EXT_UCODE_3: + ucode_rev += 1; + case EXT_UCODE_2: + ucode_rev += 1; + case EXT_UCODE_1: + ucode_rev += 1; + data_length = extract->length; + if (flags & FW_FLAG_LE) + swap_endianness_ucode(buf, data_length); + analyse_ucode(ucode_rev, buf, data_length); + hdr.type = FW_TYPE_UCODE; + hdr.size = to_be32(data_length); + break; + case EXT_PCM: + data_length = extract->length; + if (flags & FW_FLAG_LE) + swap_endianness_pcm(buf, data_length); + hdr.type = FW_TYPE_PCM; + hdr.size = to_be32(data_length); + break; + case EXT_IV: { + struct b43_iv *ivs; + + hdr.type = FW_TYPE_IV; + build_ivs(&ivs, &data_length, + (struct iv *)buf, extract->length, + &hdr, flags); + free(buf); + buf = (uint8_t *)ivs; + break; + } + default: + exit(255); + } + + if (!cmdargs.identify_only) + write_file(extract->name, buf, data_length, &hdr, flags); + + free(buf); +} + +static void print_banner(void) +{ + printf("b43-fwcutter version " FWCUTTER_VERSION "\n"); +} + +static void print_file(const struct file *file) +{ + char filename[30]; + char shortname[30]; + + if (file->flags & FW_FLAG_V4) + printf(V4_FW_DIRNAME "\t\t"); + else + printf(V3_FW_DIRNAME "\t"); + + if (strlen(file->name) > 20) { + strncpy(shortname, file->name, 20); + shortname[20] = '\0'; + snprintf(filename, sizeof(filename), "%s..", shortname); + } else + strcpy (filename, file->name); + + printf("%s\t", filename); + if (strlen(filename) < 8) printf("\t"); + if (strlen(filename) < 16) printf("\t"); + + printf("%s\t", file->ucode_version); + if (strlen(file->ucode_version) < 8) printf("\t"); + + printf("%s\n", file->md5); +} + +static void print_supported_files(void) +{ + int i; + + print_banner(); + printf("\nExtracting firmware is possible " + "from these binary driver files:\n\n"); + printf("\t" + "\t\t" + "\t" + "\n\n"); + /* print for legacy driver first */ + for (i = 0; i < FILES; i++) + if (!(files[i].flags & FW_FLAG_V4)) + print_file(&files[i]); + for (i = 0; i < FILES; i++) + if (files[i].flags & FW_FLAG_V4) + print_file(&files[i]); + printf("\n"); +} + +static const struct file *find_file(FILE *fd) +{ + unsigned char buffer[16384], signature[16]; + struct MD5Context md5c; + char md5sig[33]; + int i; + + MD5Init(&md5c); + while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0) + MD5Update(&md5c, buffer, (unsigned) i); + MD5Final(signature, &md5c); + + snprintf(md5sig, sizeof(md5sig), + "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x" + "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", + signature[0], signature[1], signature[2], signature[3], + signature[4], signature[5], signature[6], signature[7], + signature[8], signature[9], signature[10], signature[11], + signature[12], signature[13], signature[14], signature[15]); + + for (i = 0; i < FILES; ++i) { + if (strcasecmp(md5sig, files[i].md5) == 0) { + printf("This file is recognised as:\n"); + printf(" filename : %s\n", files[i].name); + printf(" version : %s\n", files[i].ucode_version); + printf(" MD5 : %s\n", files[i].md5); + return &files[i]; + } + } + printf("Sorry, the input file is either wrong or " + "not supported by b43-fwcutter.\n"); + printf("This file has an unknown MD5sum %s.\n", md5sig); + + return NULL; +} + +static void print_usage(int argc, char *argv[]) +{ + print_banner(); + printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]); + printf(" -l|--list " + "List supported driver versions\n"); + printf(" -i|--identify " + "Only identify the driver file (don't extract)\n"); + printf(" -w|--target-dir DIR " + "Extract and write firmware to DIR\n"); + printf(" -v|--version " + "Print b43-fwcutter version\n"); + printf(" -h|--help " + "Print this help\n"); + printf("\nExample: %s bcmwl5.sys\n" + " to extract the firmware blobs from bcmwl5.sys\n", + argv[0]); +} + +static int do_cmp_arg(char **argv, int *pos, + const char *template, + int allow_merged, + char **param) +{ + char *arg; + char *next_arg; + size_t arg_len, template_len; + + arg = argv[*pos]; + next_arg = argv[*pos + 1]; + arg_len = strlen(arg); + template_len = strlen(template); + + if (param) { + /* Maybe we have a merged parameter here. + * A merged parameter is "-pfoobar" for example. + */ + if (allow_merged && arg_len > template_len) { + if (memcmp(arg, template, template_len) == 0) { + *param = arg + template_len; + return ARG_MATCH; + } + return ARG_NOMATCH; + } else if (arg_len != template_len) + return ARG_NOMATCH; + *param = next_arg; + } + if (strcmp(arg, template) == 0) { + if (param) { + /* Skip the parameter on the next iteration. */ + (*pos)++; + if (*param == 0) { + printf("%s needs a parameter\n", arg); + return ARG_ERROR; + } + } + return ARG_MATCH; + } + + return ARG_NOMATCH; +} + +/* Simple and lean command line argument parsing. */ +static int cmp_arg(char **argv, int *pos, + const char *long_template, + const char *short_template, + char **param) +{ + int err; + + if (long_template) { + err = do_cmp_arg(argv, pos, long_template, 0, param); + if (err == ARG_MATCH || err == ARG_ERROR) + return err; + } + err = ARG_NOMATCH; + if (short_template) + err = do_cmp_arg(argv, pos, short_template, 1, param); + return err; +} + +static int parse_args(int argc, char *argv[]) +{ + int i, res; + char *param; + + if (argc < 2) + goto out_usage; + for (i = 1; i < argc; i++) { + res = cmp_arg(argv, &i, "--list", "-l", 0); + if (res == ARG_MATCH) { + print_supported_files(); + return 1; + } else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--version", "-v", 0); + if (res == ARG_MATCH) { + print_banner(); + return 1; + } else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--help", "-h", 0); + if (res == ARG_MATCH) + goto out_usage; + else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--identify", "-i", 0); + if (res == ARG_MATCH) { + cmdargs.identify_only = 1; + continue; + } else if (res == ARG_ERROR) + goto out; + + res = cmp_arg(argv, &i, "--target-dir", "-w", ¶m); + if (res == ARG_MATCH) { + cmdargs.target_dir = param; + continue; + } else if (res == ARG_ERROR) + goto out; + + cmdargs.infile = argv[i]; + break; + } + + if (!cmdargs.infile) + goto out_usage; + return 0; + +out_usage: + print_usage(argc, argv); +out: + return -1; +} + +int main(int argc, char *argv[]) +{ + FILE *fd; + const struct file *file; + const struct extract *extract; + int err; + const char *dir; + + cmdargs.target_dir = "."; + err = parse_args(argc, argv); + if (err == 1) + return 0; + else if (err != 0) + return err; + + fd = fopen(cmdargs.infile, "rb"); + if (!fd) { + fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile); + return 2; + } + + err = -1; + file = find_file(fd); + if (!file) + goto out_close; + + if (file->flags & FW_FLAG_V4) + dir = V4_FW_DIRNAME; + else + dir = V3_FW_DIRNAME; + + extract = file->extract; + while (extract->name) { + printf("%s %s/%s.fw\n", + cmdargs.identify_only ? "Contains" : "Extracting", + dir, extract->name); + extract_or_identify(fd, extract, file->flags); + extract++; + } + + err = 0; +out_close: + fclose(fd); + + return err; +} diff --git a/fwcutter/fwcutter.h b/fwcutter/fwcutter.h new file mode 100644 index 0000000..2f23d81 --- /dev/null +++ b/fwcutter/fwcutter.h @@ -0,0 +1,97 @@ +#ifndef _FWCUTTER_H_ +#define _FWCUTTER_H_ + +#define FW_FLAG_LE 0x01 /* little endian? convert */ +#define FW_FLAG_V4 0x02 /* b43 vs. b43legacy */ + +#define fwcutter_stringify_1(x) #x +#define fwcutter_stringify(x) fwcutter_stringify_1(x) +#define FWCUTTER_VERSION fwcutter_stringify(FWCUTTER_VERSION_) + +typedef uint16_t be16_t; /* Big-endian 16bit */ +typedef uint32_t be32_t; /* Big-endian 32bit */ + +#ifdef __DragonFly__ +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#endif + +#define ARG_MATCH 0 +#define ARG_NOMATCH 1 +#define ARG_ERROR -1 + +struct cmdline_args { + const char *infile; + const char *target_dir; + int identify_only; +}; + +struct insn { + uint32_t opcode; + uint16_t op1, op2, op3; +}; + +/* The IV how it's done in the binary driver files. */ +struct iv { + be16_t reg, size; + be32_t val; +} __attribute__((__packed__)); + +enum extract_type { + EXT_UNDEFINED, /* error catcher */ + EXT_UCODE_1, /* rev <= 4 ucode */ + EXT_UCODE_2, /* rev 5..14 ucode */ + EXT_UCODE_3, /* rev >= 15 ucode */ + EXT_PCM, /* "pcm" values */ + EXT_IV, /* initial values */ +}; + +struct extract { + const char *name; + const uint32_t offset; + const uint32_t length; + const enum extract_type type; +}; + +#define EXTRACT_LIST_END { .name = NULL, } + +struct file { + const char *name; + const char *ucode_version; + const char *md5; + const struct extract *extract; + const uint32_t flags; +}; + +/* The header that's put in to every .fw file */ +struct fw_header { + /* Type of the firmware data */ + uint8_t type; + /* Version number of the firmware data format */ + uint8_t ver; + uint8_t __padding[2]; + /* Size of the data. For ucode and PCM this is in bytes. + * For IV this is in number-of-ivs. */ + be32_t size; +} __attribute__((__packed__)); + +#define FW_TYPE_UCODE 'u' +#define FW_TYPE_PCM 'p' +#define FW_TYPE_IV 'i' + +#define FW_HDR_VER 0x01 + +/* The IV in the .fw file */ +struct b43_iv { + be16_t offset_size; + union { + be16_t d16; + be32_t d32; + } data __attribute__((__packed__)); +} __attribute__((__packed__)); + +#define FW_IV_OFFSET_MASK 0x7FFF +#define FW_IV_32BIT 0x8000 + + +#endif /* _FWCUTTER_H_ */ diff --git a/fwcutter/fwcutter_list.h b/fwcutter/fwcutter_list.h new file mode 100644 index 0000000..c3631ce --- /dev/null +++ b/fwcutter/fwcutter_list.h @@ -0,0 +1,155 @@ + +/* file member lists */ +struct extract _e08665c5c5b66beb9c3b2dd54aa80cb3[] = +{ + { .name = "ucode2", .offset = 0x59ca0, .length = 0x3fe0, .type = EXT_UCODE_1, }, + { .name = "ucode4", .offset = 0x5dc84, .length = 0x4e78, .type = EXT_UCODE_1, }, + { .name = "ucode5", .offset = 0x62b00, .length = 0x5700, .type = EXT_UCODE_2, }, + { .name = "ucode11", .offset = 0x68204, .length = 0x54a8, .type = EXT_UCODE_2, }, + { .name = "pcm4", .offset = 0x6d6b0, .length = 0x520, .type = EXT_PCM, }, + { .name = "pcm5", .offset = 0x6dbd4, .length = 0x520, .type = EXT_PCM, }, + { .name = "a0g0bsinitvals2", .offset = 0x54ad0 + 0x30d8, .length = 0x18 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals5", .offset = 0x54ad0 + 0x3ae0, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals5", .offset = 0x54ad0 + 0x3be0, .length = 0x9f0 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals5", .offset = 0x54ad0 + 0x50c0, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals2", .offset = 0x54ad0 + 0x2320, .length = 0xdb8 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals5", .offset = 0x54ad0 + 0x45d0, .length = 0x9f0 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals2", .offset = 0x54ad0 + 0x2308, .length = 0x18 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals5", .offset = 0x54ad0 + 0x30f0, .length = 0x9f0 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals2", .offset = 0x54ad0 + 0x1550, .length = 0xdb8 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals5", .offset = 0x54ad0 + 0x4fc0, .length = 0x100 - 8, .type = EXT_IV, }, + EXTRACT_LIST_END +}; + +struct extract _9207bc565c2fc9fa1591f6c7911d3fc0[] = +{ + { .name = "ucode4", .offset = 0x66220 + 0x7ad8, .length = 0x4e68, .type = EXT_UCODE_1, }, + { .name = "ucode5", .offset = 0x66220 + 0xc944, .length = 0x5640, .type = EXT_UCODE_2, }, + { .name = "ucode11", .offset = 0x66220 + 0x11f90, .length = 0x67e0, .type = EXT_UCODE_2, }, + { .name = "ucode13", .offset = 0x66220 + 0x18774, .length = 0x5f60, .type = EXT_UCODE_2, }, + { .name = "pcm4", .offset = 0x66220 + 0x1e6d8, .length = 0x520, .type = EXT_PCM, }, + { .name = "pcm5", .offset = 0x66220 + 0x1ebfc, .length = 0x520, .type = EXT_PCM, }, + { .name = "b0g0initvals4", .offset = 0x66220 + 0x1710, .length = 0xe90 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals4", .offset = 0x66220 + 0x25a0, .length = 0x18 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals4", .offset = 0x66220 + 0x25b8, .length = 0xe90 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals4", .offset = 0x66220 + 0x3448, .length = 0x18 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals5", .offset = 0x66220 + 0x3460, .length = 0xa28 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals5", .offset = 0x66220 + 0x3e88, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals5", .offset = 0x66220 + 0x3f88, .length = 0xa28 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals5", .offset = 0x66220 + 0x49b0, .length = 0xa28 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals5", .offset = 0x66220 + 0x53d8, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals5", .offset = 0x66220 + 0x54d8, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "lp0initvals13", .offset = 0x66220 + 0x5620, .length = 0xb38 - 8, .type = EXT_IV, }, + { .name = "lp0bsinitvals13", .offset = 0x66220 + 0x6158, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals13", .offset = 0x66220 + 0x6258, .length = 0xb40 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals13",.offset = 0x66220 + 0x6d98, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals13", .offset = 0x66220 + 0x6e98, .length = 0xb40 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals13",.offset = 0x66220 + 0x79d8, .length = 0x100 - 8, .type = EXT_IV, }, + EXTRACT_LIST_END +}; + +struct extract _722e2e0d8cc04b8f118bb5afe6829ff9[] = +{ + { .name = "ucode4", .offset = 0x76a10 + 0x8960, .length = 0x4e68, .type = EXT_UCODE_1, }, + { .name = "ucode5", .offset = 0x76a10 + 0xd7cc, .length = 0x5640, .type = EXT_UCODE_2, }, + { .name = "ucode11", .offset = 0x76a10 + 0x12e18, .length = 0x67e0, .type = EXT_UCODE_2, }, + { .name = "ucode13", .offset = 0x76a10 + 0x195fc, .length = 0x5f60, .type = EXT_UCODE_2, }, + { .name = "pcm4", .offset = 0x76a10 + 0x1f560, .length = 0x520, .type = EXT_PCM, }, + { .name = "pcm5", .offset = 0x76a10 + 0x1fa84, .length = 0x520, .type = EXT_PCM, }, + { .name = "b0g0initvals4", .offset = 0x76a10 + 0x1840, .length = 0xe90 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals4", .offset = 0x76a10 + 0x26d0, .length = 0x18 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals4", .offset = 0x76a10 + 0x26e8, .length = 0xe90 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals4", .offset = 0x76a10 + 0x3578, .length = 0x18 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals5", .offset = 0x76a10 + 0x3590, .length = 0xa28 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals5", .offset = 0x76a10 + 0x3fb8, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals5", .offset = 0x76a10 + 0x40b8, .length = 0xa28 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals5", .offset = 0x76a10 + 0x4ae0, .length = 0xa28 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals5", .offset = 0x76a10 + 0x5508, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals5", .offset = 0x76a10 + 0x5608, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "lp0initvals13", .offset = 0x76a10 + 0x64a8, .length = 0xb38 - 8, .type = EXT_IV, }, + { .name = "lp0bsinitvals13", .offset = 0x76a10 + 0x6fe0, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals13", .offset = 0x76a10 + 0x70e0, .length = 0xb40 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals13",.offset = 0x76a10 + 0x7c20, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals13", .offset = 0x76a10 + 0x7d20, .length = 0xb40 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals13",.offset = 0x76a10 + 0x8860, .length = 0x100 - 8, .type = EXT_IV, }, + EXTRACT_LIST_END +}; + +struct extract _1e4763b4cb8cfbaae43e5c6d3d6b2ae7[] = +{ + { .name = "ucode5", .offset = 0x71c80 + 0xacd0, .length = 0x5768, .type = EXT_UCODE_2, }, + { .name = "ucode9", .offset = 0x71c80 + 0x1043c, .length = 0x6240, .type = EXT_UCODE_2, }, + { .name = "ucode11", .offset = 0x71c80 + 0x16680, .length = 0x74a0, .type = EXT_UCODE_2, }, + { .name = "ucode13", .offset = 0x71c80 + 0x1db24, .length = 0x7de0, .type = EXT_UCODE_2, }, + { .name = "ucode14", .offset = 0x71c80 + 0x25908, .length = 0x7a90, .type = EXT_UCODE_2, }, + { .name = "ucode15", .offset = 0x71c80 + 0x2d39c, .length = 0x7710, .type = EXT_UCODE_3, }, + { .name = "pcm5", .offset = 0x71c80 + 0x34ab0, .length = 0x520, .type = EXT_PCM, }, + { .name = "b0g0initvals5", .offset = 0x71c80 + 0x14d0, .length = 0xa10 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals5", .offset = 0x71c80 + 0x1ee0, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals5", .offset = 0x71c80 + 0x1fe0, .length = 0xa10 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals5", .offset = 0x71c80 + 0x29f0, .length = 0xa10 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals5", .offset = 0x71c80 + 0x3400, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals5", .offset = 0x71c80 + 0x3500, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals9", .offset = 0x71c80 + 0x3600, .length = 0xae8 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals9", .offset = 0x71c80 + 0x40e8, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g0initvals9", .offset = 0x71c80 + 0x41e8, .length = 0xae8 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals9", .offset = 0x71c80 + 0x4cd0, .length = 0xae8 - 8, .type = EXT_IV, }, + { .name = "a0g0bsinitvals9", .offset = 0x71c80 + 0x57b8, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals9", .offset = 0x71c80 + 0x58b8, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "n0initvals11", .offset = 0x71c80 + 0x59b8, .length = 0xb78 - 8, .type = EXT_IV, }, + { .name = "n0bsinitvals11", .offset = 0x71c80 + 0x6530, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "n0absinitvals11", .offset = 0x71c80 + 0x6630, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "lp0initvals13", .offset = 0x71c80 + 0x6730, .length = 0x1360 - 8, .type = EXT_IV, }, + { .name = "lp0bsinitvals13", .offset = 0x71c80 + 0x7a90, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "b0g0initvals13", .offset = 0x71c80 + 0x7b90, .length = 0xb60 - 8, .type = EXT_IV, }, + { .name = "b0g0bsinitvals13",.offset = 0x71c80 + 0x86f0, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "a0g1initvals13", .offset = 0x71c80 + 0x87f0, .length = 0xb60 - 8, .type = EXT_IV, }, + { .name = "a0g1bsinitvals13",.offset = 0x71c80 + 0x9350, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "lp0initvals14", .offset = 0x71c80 + 0x9450, .length = 0xb48 - 8, .type = EXT_IV, }, + { .name = "lp0bsinitvals14", .offset = 0x71c80 + 0x9f98, .length = 0x100 - 8, .type = EXT_IV, }, + { .name = "lp0initvals15", .offset = 0x71c80 + 0xa098, .length = 0xb38 - 8, .type = EXT_IV, }, + { .name = "lp0bsinitvals15", .offset = 0x71c80 + 0xabd0, .length = 0x100 - 8, .type = EXT_IV, }, + EXTRACT_LIST_END +}; + + +/* + * Links change, so let's not put them into the README. + * I still put them here so we know where the file was obtained. + */ +static const struct file files[] = +{ + { + .name = "wl_apsta.o", + .ucode_version = "295.14", + .md5 = "e08665c5c5b66beb9c3b2dd54aa80cb3", + .flags = FW_FLAG_LE, + .extract = _e08665c5c5b66beb9c3b2dd54aa80cb3, + }, + { + /* http://downloads.openwrt.org/sources/broadcom-wl-4.80.53.0.tar.bz2 */ + .name = "wl_apsta.o", + .ucode_version = "351.126", + .md5 = "9207bc565c2fc9fa1591f6c7911d3fc0", + .flags = FW_FLAG_LE | FW_FLAG_V4, + .extract = _9207bc565c2fc9fa1591f6c7911d3fc0, + }, + { + /* http://downloads.openwrt.org/sources/broadcom-wl-4.80.53.0.tar.bz2 */ + .name = "wl_apsta_mimo.o", + .ucode_version = "351.126", + .md5 = "722e2e0d8cc04b8f118bb5afe6829ff9", + .flags = FW_FLAG_LE | FW_FLAG_V4, + .extract = _722e2e0d8cc04b8f118bb5afe6829ff9, + }, + { + /* ftp://ftp.linksys.com/opensourcecode/wrt150nv11/1.51.3/ */ + .name = "wl_ap.o", + .ucode_version = "410.2160", + .md5 = "1e4763b4cb8cfbaae43e5c6d3d6b2ae7", + .flags = FW_FLAG_LE | FW_FLAG_V4, + .extract = _1e4763b4cb8cfbaae43e5c6d3d6b2ae7, + }, +}; + +#define FILES (sizeof(files) / sizeof(files[0])) diff --git a/fwcutter/md5.c b/fwcutter/md5.c new file mode 100644 index 0000000..3b90942 --- /dev/null +++ b/fwcutter/md5.c @@ -0,0 +1,248 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Brutally hacked by John Walker back from ANSI C to K&R (no + prototypes) to maintain the tradition that Netfone will compile + with Sun's original "cc". */ +/* Ripped out ugly K&R again ;) --mbuesch */ +/* killed stupid endianness thing --jmberg */ + +#include /* for memcpy() */ +#include "md5.h" + +static void byteReverse(unsigned char *buf, unsigned longs) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN +/* we need the value in big-endian */ + uint32_t t; + + do { + t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + } while (--longs); +#endif +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32_t *buf, uint32_t *in) +{ + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char *digest, struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32_t *) ctx->in)[14] = ctx->bits[0]; + ((uint32_t *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} diff --git a/fwcutter/md5.h b/fwcutter/md5.h new file mode 100644 index 0000000..ee574e4 --- /dev/null +++ b/fwcutter/md5.h @@ -0,0 +1,16 @@ +#ifndef FWCUTTER_MD5_H_ +#define FWCUTTER_MD5_H_ + +#include + +struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +}; + +void MD5Init(struct MD5Context *ctx); +void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len); +void MD5Final(unsigned char *digest, struct MD5Context *ctx); + +#endif /* FWCUTTER_MD5_H_ */ diff --git a/ssb_sprom/COPYING b/ssb_sprom/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/ssb_sprom/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ssb_sprom/Makefile b/ssb_sprom/Makefile new file mode 100644 index 0000000..d689120 --- /dev/null +++ b/ssb_sprom/Makefile @@ -0,0 +1,26 @@ +VERSION = 001 + +CC = cc +PREFIX = /usr/local +CFLAGS = -std=c99 -O2 -fomit-frame-pointer -Wall -D_BSD_SOURCE +LDFLAGS = + + +OBJECTS = ssb_sprom.o utils.o + +CFLAGS += -DVERSION_=$(VERSION) + +all: ssb_sprom + +ssb_sprom: $(OBJECTS) + $(CC) $(CFLAGS) -o ssb-sprom $(OBJECTS) $(LDFLAGS) + +install: all + -install -o 0 -g 0 -m 755 ssb-sprom $(PREFIX)/bin/ + +clean: + -rm -f *~ *.o *.orig *.rej ssb-sprom + +# dependencies +ssb_sprom.o: ssb_sprom.h utils.h +utils.o: utils.h ssb_sprom.h diff --git a/ssb_sprom/README b/ssb_sprom/README new file mode 100644 index 0000000..f1c2edf --- /dev/null +++ b/ssb_sprom/README @@ -0,0 +1,15 @@ + + Broadcom-SSB SPROM data modification tool + ========================================= + +About this software +------------------- + +A tool for the convenient modification of the +Broadcom Sonics Silicon Backplane SPROM. +This tool automatically adjusts the CRC after the modification. + +Requirements +------------ + +1) C99 compatible compiler. diff --git a/ssb_sprom/ssb_sprom.c b/ssb_sprom/ssb_sprom.c new file mode 100644 index 0000000..51124f2 --- /dev/null +++ b/ssb_sprom/ssb_sprom.c @@ -0,0 +1,1420 @@ +/* + + Broadcom Sonics Silicon Backplane bus SPROM data modification tool + + Copyright (c) 2006-2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "ssb_sprom.h" +#include "utils.h" + +#include +#include +#include +#include +#include + + +struct cmdline_args cmdargs; + +static int value_length_map[] = { /* value to number of bits */ + [VALUE_RAW] = 8, + [VALUE_SUBP] = 16, + [VALUE_SUBV] = 16, + [VALUE_PPID] = 16, + [VALUE_BFLHI] = 16, + [VALUE_BFL] = 16, + [VALUE_BGMAC] = -1, + [VALUE_ETMAC] = -1, + [VALUE_AMAC] = -1, + [VALUE_ET0PHY] = 8, + [VALUE_ET1PHY] = 8, + [VALUE_ET0MDC] = 1, + [VALUE_ET1MDC] = 1, + [VALUE_BREV] = 8, + [VALUE_LOC] = 4, + [VALUE_ANTA0] = 1, + [VALUE_ANTA1] = 1, + [VALUE_ANTBG0] = 1, + [VALUE_ANTBG1] = 1, + [VALUE_ANTGA] = 8, + [VALUE_ANTGBG] = 8, + [VALUE_PA0B0] = 16, + [VALUE_PA0B1] = 16, + [VALUE_PA0B2] = 16, + [VALUE_PA1B0] = 16, + [VALUE_PA1B1] = 16, + [VALUE_PA1B2] = 16, + [VALUE_WL0GPIO0] = 8, + [VALUE_WL0GPIO1] = 8, + [VALUE_WL0GPIO2] = 8, + [VALUE_WL0GPIO3] = 8, + [VALUE_MAXPA] = 8, + [VALUE_MAXPBG] = 8, + [VALUE_ITSSIA] = 8, + [VALUE_ITSSIBG] = 8, + [VALUE_SVER] = 8, +}; + + +static int hexdump_sprom(const uint8_t *sprom, char *buffer, size_t bsize) +{ + int i, pos = 0; + + for (i = 0; i < SPROM_SIZE; i++) { + pos += snprintf(buffer + pos, bsize - pos - 1, + "%02X", sprom[i] & 0xFF); + } + + return pos + 1; +} + +static uint8_t sprom_crc(const uint8_t *sprom) +{ + int i; + uint8_t crc = 0xFF; + + for (i = 0; i < SPROM_SIZE - 1; i++) + crc = crc8(crc, sprom[i]); + crc ^= 0xFF; + + return crc; +} + +static int write_output_binary(int fd, const uint8_t *sprom) +{ + ssize_t w; + + w = write(fd, sprom, SPROM_SIZE); + if (w < 0) + return -1; + + return 0; +} + +static int write_output_hex(int fd, const uint8_t *sprom) +{ + ssize_t w; + char tmp[SPROM_SIZE * 2 + 10] = { 0 }; + + hexdump_sprom(sprom, tmp, sizeof(tmp)); + prinfo("Raw output: %s\n", tmp); + w = write(fd, tmp, SPROM_SIZE * 2); + if (w < 0) + return -1; + + return 0; +} + +static int write_output(int fd, const uint8_t *sprom) +{ + int err; + + if (cmdargs.outfile) { + err = ftruncate(fd, 0); + if (err) { + prerror("Could not truncate --outfile %s\n", + cmdargs.outfile); + return -1; + } + } + + if (cmdargs.bin_mode) + err = write_output_binary(fd, sprom); + else + err = write_output_hex(fd, sprom); + if (err) + prerror("Could not write output data.\n"); + + return err; +} + +static int modify_value(uint8_t *sprom, + struct cmdline_vparm *vparm) +{ + const uint16_t v = vparm->u.value; + uint16_t tmp = 0; + + switch (vparm->type) { + case VALUE_RAW: + sprom[vparm->u.raw.offset] = vparm->u.raw.value; + break; + case VALUE_SUBP: + sprom[SPROM_SUBP + 0] = (v & 0x00FF); + sprom[SPROM_SUBP + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_SUBV: + sprom[SPROM_SUBV + 0] = (v & 0x00FF); + sprom[SPROM_SUBV + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_PPID: + sprom[SPROM_PPID + 0] = (v & 0x00FF); + sprom[SPROM_PPID + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_BFLHI: + sprom[SPROM_BFLHI + 0] = (v & 0x00FF); + sprom[SPROM_BFLHI + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_BFL: + sprom[SPROM_BOARDFLAGS + 0] = (v & 0x00FF); + sprom[SPROM_BOARDFLAGS + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_BGMAC: + sprom[SPROM_IL0MACADDR + 1] = vparm->u.mac[0]; + sprom[SPROM_IL0MACADDR + 0] = vparm->u.mac[1]; + sprom[SPROM_IL0MACADDR + 3] = vparm->u.mac[2]; + sprom[SPROM_IL0MACADDR + 2] = vparm->u.mac[3]; + sprom[SPROM_IL0MACADDR + 5] = vparm->u.mac[4]; + sprom[SPROM_IL0MACADDR + 4] = vparm->u.mac[5]; + break; + case VALUE_ETMAC: + sprom[SPROM_ET0MACADDR + 1] = vparm->u.mac[0]; + sprom[SPROM_ET0MACADDR + 0] = vparm->u.mac[1]; + sprom[SPROM_ET0MACADDR + 3] = vparm->u.mac[2]; + sprom[SPROM_ET0MACADDR + 2] = vparm->u.mac[3]; + sprom[SPROM_ET0MACADDR + 5] = vparm->u.mac[4]; + sprom[SPROM_ET0MACADDR + 4] = vparm->u.mac[5]; + break; + case VALUE_AMAC: + sprom[SPROM_ET1MACADDR + 1] = vparm->u.mac[0]; + sprom[SPROM_ET1MACADDR + 0] = vparm->u.mac[1]; + sprom[SPROM_ET1MACADDR + 3] = vparm->u.mac[2]; + sprom[SPROM_ET1MACADDR + 2] = vparm->u.mac[3]; + sprom[SPROM_ET1MACADDR + 5] = vparm->u.mac[4]; + sprom[SPROM_ET1MACADDR + 4] = vparm->u.mac[5]; + break; + case VALUE_ET0PHY: + tmp |= sprom[SPROM_ETHPHY + 0]; + tmp |= sprom[SPROM_ETHPHY + 1] << 8; + tmp = ((tmp & 0x001F) | (v & 0x1F)); + sprom[SPROM_ETHPHY + 0] = (tmp & 0x00FF); + sprom[SPROM_ETHPHY + 1] = (tmp & 0xFF00) >> 8; + break; + case VALUE_ET1PHY: + tmp |= sprom[SPROM_ETHPHY + 0]; + tmp |= sprom[SPROM_ETHPHY + 1] << 8; + tmp = ((tmp & 0x03E0) | ((v & 0x1F) << 5)); + sprom[SPROM_ETHPHY + 0] = (tmp & 0x00FF); + sprom[SPROM_ETHPHY + 1] = (tmp & 0xFF00) >> 8; + break; + case VALUE_ET0MDC: + sprom[SPROM_ETHPHY + 1] &= ~(1 << 6); + if (v) + sprom[SPROM_ETHPHY + 1] |= (1 << 6); + break; + case VALUE_ET1MDC: + sprom[SPROM_ETHPHY + 1] &= ~(1 << 7); + if (v) + sprom[SPROM_ETHPHY + 1] |= (1 << 7); + break; + case VALUE_BREV: + sprom[SPROM_BOARDREV + 0] = v; + break; + case VALUE_LOC: + tmp = (sprom[SPROM_BOARDREV + 1] & 0xF0); + tmp |= (v & 0x0F); + sprom[SPROM_BOARDREV + 1] = (tmp & 0xFF); + break; + case VALUE_ANTA0: + sprom[SPROM_BOARDREV + 1] &= ~(1 << 4); + if (v) + sprom[SPROM_BOARDREV + 1] |= (1 << 4); + break; + case VALUE_ANTA1: + sprom[SPROM_BOARDREV + 1] &= ~(1 << 5); + if (v) + sprom[SPROM_BOARDREV + 1] |= (1 << 5); + break; + case VALUE_ANTBG0: + sprom[SPROM_BOARDREV + 1] &= ~(1 << 6); + if (v) + sprom[SPROM_BOARDREV + 1] |= (1 << 6); + break; + case VALUE_ANTBG1: + sprom[SPROM_BOARDREV + 1] &= ~(1 << 7); + if (v) + sprom[SPROM_BOARDREV + 1] |= (1 << 7); + break; + case VALUE_ANTGA: + sprom[SPROM_ANTENNA_GAIN + 0] = (v & 0xFF); + break; + case VALUE_ANTGBG: + sprom[SPROM_ANTENNA_GAIN + 1] = (v & 0xFF); + break; + case VALUE_PA0B0: + sprom[SPROM_PA0B0 + 0] = (v & 0x00FF); + sprom[SPROM_PA0B0 + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_PA0B1: + sprom[SPROM_PA0B1 + 0] = (v & 0x00FF); + sprom[SPROM_PA0B1 + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_PA0B2: + sprom[SPROM_PA0B2 + 0] = (v & 0x00FF); + sprom[SPROM_PA0B2 + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_PA1B0: + sprom[SPROM_PA1B0 + 0] = (v & 0x00FF); + sprom[SPROM_PA1B0 + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_PA1B1: + sprom[SPROM_PA1B1 + 0] = (v & 0x00FF); + sprom[SPROM_PA1B1 + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_PA1B2: + sprom[SPROM_PA1B2 + 0] = (v & 0x00FF); + sprom[SPROM_PA1B2 + 1] = (v & 0xFF00) >> 8; + break; + case VALUE_WL0GPIO0: + sprom[SPROM_WL0GPIO0 + 0] = (v & 0xFF); + break; + case VALUE_WL0GPIO1: + sprom[SPROM_WL0GPIO0 + 1] = (v & 0xFF); + break; + case VALUE_WL0GPIO2: + sprom[SPROM_WL0GPIO2 + 0] = (v & 0xFF); + break; + case VALUE_WL0GPIO3: + sprom[SPROM_WL0GPIO2 + 1] = (v & 0xFF); + break; + case VALUE_MAXPA: + sprom[SPROM_MAXPWR + 0] = (v & 0xFF); + break; + case VALUE_MAXPBG: + sprom[SPROM_MAXPWR + 1] = (v & 0xFF); + break; + case VALUE_ITSSIA: + sprom[SPROM_IDL_TSSI_TGT + 0] = (v & 0xFF); + break; + case VALUE_ITSSIBG: + sprom[SPROM_IDL_TSSI_TGT + 1] = (v & 0xFF); + break; + case VALUE_SVER: + sprom[SPROM_VERSION + 0] = (v & 0xFF); + break; + default: + prerror("vparm->type internal error (0)\n"); + exit(1); + } + + return 0; +} + +static int modify_sprom(uint8_t *sprom) +{ + struct cmdline_vparm *vparm; + int i; + int modified = 0; + uint8_t crc; + + for (i = 0; i < cmdargs.nr_vparm; i++) { + vparm = &(cmdargs.vparm[i]); + if (!vparm->set) + continue; + modify_value(sprom, vparm); + modified = 1; + } + if (modified) { + /* Recalculate the CRC. */ + crc = sprom_crc(sprom); + sprom[SPROM_VERSION + 1] = crc; + } + + return modified; +} + +static void display_value(const uint8_t *sprom, + struct cmdline_vparm *vparm) +{ + const char *desc; + uint8_t offset; + uint16_t value; + uint16_t tmp; + + switch (vparm->type) { + case VALUE_RAW: + desc = "RAW"; + offset = vparm->u.raw.offset; + value = sprom[offset]; + break; + case VALUE_SUBP: + desc = "Subsytem product ID"; + offset = SPROM_SUBP; + value = sprom[SPROM_SUBP + 0]; + value |= sprom[SPROM_SUBP + 1] << 8; + break; + case VALUE_SUBV: + desc = "Subsystem vendor ID"; + offset = SPROM_SUBV; + value = sprom[SPROM_SUBV + 0]; + value |= sprom[SPROM_SUBV + 1] << 8; + break; + case VALUE_PPID: + desc = "PCI Product ID"; + offset = SPROM_PPID; + value = sprom[SPROM_PPID + 0]; + value |= sprom[SPROM_PPID + 1] << 8; + break; + case VALUE_BFLHI: + desc = "High 16 bits of Boardflags"; + offset = SPROM_BFLHI; + value = sprom[SPROM_BFLHI + 0]; + value |= sprom[SPROM_BFLHI + 1] << 8; + break; + case VALUE_BFL: + desc = "Low 16 bits of Boardflags"; + offset = SPROM_BOARDFLAGS; + value = sprom[SPROM_BOARDFLAGS + 0]; + value |= sprom[SPROM_BOARDFLAGS + 1] << 8; + break; + case VALUE_BGMAC: + desc = "MAC address for 802.11b/g"; + offset = SPROM_IL0MACADDR; + value = 0; + break; + case VALUE_ETMAC: + desc = "MAC address for ethernet"; + offset = SPROM_ET0MACADDR; + value = 0; + break; + case VALUE_AMAC: + desc = "MAC address for 802.11a"; + offset = SPROM_ET1MACADDR; + value = 0; + break; + case VALUE_ET0PHY: + desc = "Ethernet phy settings (0)"; + offset = SPROM_ETHPHY; + tmp = sprom[SPROM_ETHPHY + 0]; + tmp |= sprom[SPROM_ETHPHY + 1] << 8; + value = (tmp & 0x001F); + break; + case VALUE_ET1PHY: + desc = "Ethernet phy settings (1)"; + offset = SPROM_ETHPHY; + tmp = sprom[SPROM_ETHPHY + 0]; + tmp |= sprom[SPROM_ETHPHY + 1] << 8; + value = (tmp & 0x03E0) >> 5; + break; + case VALUE_ET0MDC: + desc = "et0mdcport"; + offset = SPROM_ETHPHY + 1; + value = 0; + if (sprom[SPROM_ETHPHY + 1] & (1 << 6)) + value = 1; + break; + case VALUE_ET1MDC: + desc = "et1mdcport"; + offset = SPROM_ETHPHY + 1; + value = 0; + if (sprom[SPROM_ETHPHY + 1] & (1 << 7)) + value = 1; + break; + case VALUE_BREV: + desc = "Board revision"; + offset = SPROM_BOARDREV; + value = sprom[SPROM_BOARDREV + 0]; + break; + case VALUE_LOC: + desc = "Locale / Country Code"; + offset = SPROM_BOARDREV + 1; + value = (sprom[SPROM_BOARDREV + 1] & 0x0F); + break; + case VALUE_ANTA0: + desc = "A PHY antenna 0 available"; + offset = SPROM_BOARDREV + 1; + value = 0; + if (sprom[SPROM_BOARDREV + 1] & (1 << 4)) + value = 1; + break; + case VALUE_ANTA1: + desc = "A PHY antenna 1 available"; + offset = SPROM_BOARDREV + 1; + value = 0; + if (sprom[SPROM_BOARDREV + 1] & (1 << 5)) + value = 1; + break; + case VALUE_ANTBG0: + desc = "B/G PHY antenna 0 available"; + offset = SPROM_BOARDREV + 1; + value = 0; + if (sprom[SPROM_BOARDREV + 1] & (1 << 6)) + value = 1; + break; + case VALUE_ANTBG1: + desc = "B/G PHY antenna 1 available"; + offset = SPROM_BOARDREV + 1; + value = 0; + if (sprom[SPROM_BOARDREV + 1] & (1 << 7)) + value = 1; + break; + case VALUE_ANTGA: + desc = "A PHY antenna gain"; + offset = SPROM_ANTENNA_GAIN; + value = sprom[SPROM_ANTENNA_GAIN]; + break; + case VALUE_ANTGBG: + desc = "B/G PHY antenna gain"; + offset = SPROM_ANTENNA_GAIN + 1; + value = sprom[SPROM_ANTENNA_GAIN + 1]; + break; + case VALUE_PA0B0: + desc = "pa0b0"; + offset = SPROM_PA0B0; + value = sprom[offset + 0]; + value |= sprom[offset + 1] << 8; + break; + case VALUE_PA0B1: + desc = "pa0b1"; + offset = SPROM_PA0B1; + value = sprom[offset + 0]; + value |= sprom[offset + 1] << 8; + break; + case VALUE_PA0B2: + desc = "pa0b2"; + offset = SPROM_PA0B2; + value = sprom[offset + 0]; + value |= sprom[offset + 1] << 8; + break; + case VALUE_PA1B0: + desc = "pa1b0"; + offset = SPROM_PA1B0; + value = sprom[offset + 0]; + value |= sprom[offset + 1] << 8; + break; + case VALUE_PA1B1: + desc = "pa1b1"; + offset = SPROM_PA1B1; + value = sprom[offset + 0]; + value |= sprom[offset + 1] << 8; + break; + case VALUE_PA1B2: + desc = "pa1b2"; + offset = SPROM_PA1B2; + value = sprom[offset + 0]; + value |= sprom[offset + 1] << 8; + break; + case VALUE_WL0GPIO0: + desc = "LED 0 behaviour"; + offset = SPROM_WL0GPIO0 + 0; + value = sprom[offset]; + break; + case VALUE_WL0GPIO1: + desc = "LED 1 behaviour"; + offset = SPROM_WL0GPIO0 + 1; + value = sprom[offset]; + break; + case VALUE_WL0GPIO2: + desc = "LED 2 behaviour"; + offset = SPROM_WL0GPIO2 + 0; + value = sprom[offset]; + break; + case VALUE_WL0GPIO3: + desc = "LED 3 behaviour"; + offset = SPROM_WL0GPIO2 + 1; + value = sprom[offset]; + break; + case VALUE_MAXPA: + desc = "A PHY max powerout"; + offset = SPROM_MAXPWR + 0; + value = sprom[offset]; + break; + case VALUE_MAXPBG: + desc = "B/G PHY max powerout"; + offset = SPROM_MAXPWR + 1; + value = sprom[offset]; + break; + case VALUE_ITSSIA: + desc = "A PHY idle TSSI target"; + offset = SPROM_IDL_TSSI_TGT + 0; + value = sprom[offset]; + break; + case VALUE_ITSSIBG: + desc = "B/G PHY idle TSSI target"; + offset = SPROM_IDL_TSSI_TGT + 1; + value = sprom[offset]; + break; + case VALUE_SVER: + desc = "SPROM version"; + offset = SPROM_VERSION; + value = sprom[offset]; + break; + default: + prerror("vparm->type internal error (1)\n"); + exit(1); + } + + switch (vparm->bits) { + case 1: + prdata("SPROM(0x%02X, %s) = %s\n", + offset, desc, value ? "ON" : "OFF"); + break; + case 4: + prdata("SPROM(0x%02X, %s) = 0x%01X\n", + offset, desc, (value & 0xF)); + break; + case 8: + prdata("SPROM(0x%02X, %s) = 0x%02X\n", + offset, desc, (value & 0xFF)); + break; + case 16: + prdata("SPROM(0x%02X, %s) = 0x%04X\n", + offset, desc, (value & 0xFFFF)); + break; + case -1: { + /* MAC address. */ + const uint8_t *p = &(sprom[offset]); + + prdata("SPROM(0x%02X, %s) = %02x:%02x:%02x:%02x:%02x:%02x\n", + offset, desc, + p[1], p[0], p[3], p[2], p[5], p[4]); + break; + } + default: + prerror("vparm->bits internal error (%d)\n", + vparm->bits); + exit(1); + } +} + +static int display_sprom(const uint8_t *sprom) +{ + struct cmdline_vparm *vparm; + int i; + + for (i = 0; i < cmdargs.nr_vparm; i++) { + vparm = &(cmdargs.vparm[i]); + if (vparm->set) + continue; + display_value(sprom, vparm); + } + + return 0; +} + +static int validate_input(const uint8_t *sprom) +{ + uint8_t crc, expected_crc; + + crc = sprom_crc(sprom); + expected_crc = sprom[SPROM_VERSION + 1]; + if (crc != expected_crc) { + prerror("Corrupt input data (crc: 0x%02X, expected: 0x%02X)\n", + crc, expected_crc); + if (!cmdargs.force) + return 1; + } + + return 0; +} + +static int parse_input(uint8_t *sprom, char *buffer, size_t bsize) +{ + char *input; + size_t inlen; + size_t cnt; + unsigned long parsed; + char tmp[SPROM_SIZE * 2 + 10] = { 0 }; + + if (cmdargs.bin_mode) { + /* The input buffer already contains + * the binary sprom data. + */ + internal_error_on(bsize != SPROM_SIZE); + memcpy(sprom, buffer, SPROM_SIZE); + return 0; + } + + inlen = bsize; + input = strchr(buffer, ':'); + if (input) { + input++; + inlen -= input - buffer; + } else + input = buffer; + + if (inlen < SPROM_SIZE * 2) { + prerror("Input data too short\n"); + return -1; + } + for (cnt = 0; cnt < SPROM_SIZE; cnt++) { + memcpy(tmp, input + cnt * 2, 2); + parsed = strtoul(tmp, NULL, 16); + sprom[cnt] = parsed & 0xFF; + } + + if (cmdargs.verbose) { + hexdump_sprom(sprom, tmp, sizeof(tmp)); + prinfo("Raw input: %s\n", tmp); + } + + return 0; +} + +static int read_infile(int fd, char **buffer, size_t *bsize) +{ + struct stat s; + int err; + ssize_t r; + + err = fstat(fd, &s); + if (err) { + prerror("Could not stat input file.\n"); + return err; + } + if (s.st_size == 0) { + prerror("No input data\n"); + return -1; + } + if (cmdargs.bin_mode) { + if (s.st_size != SPROM_SIZE) { + prerror("The input data is no SPROM Binary data. " + "The size must be exactly %d bytes, " + "but it is %u bytes\n", + SPROM_SIZE, (unsigned int)(s.st_size)); + return -1; + } + } else { + if (s.st_size > 1024 * 1024) { + prerror("The input data does not look " + "like SPROM HEX data (too long).\n"); + return -1; + } + } + + *bsize = s.st_size; + if (!cmdargs.bin_mode) + (*bsize)++; + *buffer = malloce(*bsize); + r = read(fd, *buffer, s.st_size); + if (r != s.st_size) { + prerror("Could not read input data.\n"); + return -1; + } + if (!cmdargs.bin_mode) + (*buffer)[r] = '\0'; + + return 0; +} + +static void close_infile(int fd) +{ + if (cmdargs.infile) + close(fd); +} + +static void close_outfile(int fd) +{ + if (cmdargs.outfile) + close(fd); +} + +static int open_infile(int *fd) +{ + *fd = STDIN_FILENO; + if (!cmdargs.infile) + return 0; + *fd = open(cmdargs.infile, O_RDONLY); + if (*fd < 0) { + prerror("Could not open --infile %s\n", + cmdargs.infile); + return -1; + } + + return 0; +} + +static int open_outfile(int *fd) +{ + *fd = STDOUT_FILENO; + if (!cmdargs.outfile) + return 0; + *fd = open(cmdargs.outfile, O_RDWR | O_CREAT, 0644); + if (*fd < 0) { + prerror("Could not open --outfile %s\n", + cmdargs.outfile); + return -1; + } + + return 0; +} + +static void print_banner(int forceprint) +{ + const char *str = "Broadcom-SSB SPROM data modification tool version " VERSION "\n"; + if (forceprint) + prdata(str); + else + prinfo(str); +} + +static void print_usage(int argc, char *argv[]) +{ + print_banner(1); + prdata("\nUsage: %s [OPTION]\n", argv[0]); + prdata(" -i|--input FILE Input file\n"); + prdata(" -o|--output FILE Output file\n"); + prdata(" -b|--binmode The Input data is plain binary data and Output will be binary\n"); + prdata(" -V|--verbose Be verbose\n"); + prdata(" -f|--force Override error checks\n"); + prdata(" -v|--version Print version\n"); + prdata(" -h|--help Print this help\n"); + prdata("\n"); + prdata("Value Parameters:\n"); + prdata("\n"); + prdata(" -s|--rawset OFF,VAL Set a VALue at a byte-OFFset\n"); + prdata(" -g|--rawget OFF Get a value at a byte-OFFset\n"); + prdata("\n"); + prdata("Predefined values (for displaying (GET) or modification):\n"); + prdata(" --subp [0xFFFF] Subsytem product ID for PCI\n"); + prdata(" --subv [0xFFFF] Subsystem vendor ID for PCI\n"); + prdata(" --ppid [0xFFFF] Product ID for PCI\n"); + prdata(" --bflhi [0xFFFF] High 16 bits of boardflags (only if spromversion > 1)\n"); + prdata(" --bfl [0xFFFF] Low 16 bits of boardflags\n"); + prdata(" --bgmac [MAC-ADDR] MAC address for 802.11b/g\n"); + prdata(" --etmac [MAC-ADDR] MAC address for ethernet, see b44 driver\n"); + prdata(" --amac [MAC-ADDR] Mac address for 802.11a\n"); + prdata(" --et0phy [0xFF]\n"); + prdata(" --et1phy [0xFF]\n"); + prdata(" --et0mdc [BOOL]\n"); + prdata(" --et1mdc [BOOL]\n"); + prdata(" --brev [0xFF] Board revision\n"); + prdata(" --loc [0xF] Country code\n"); + prdata(" --anta0 [BOOL] Antenna 0 available for A PHY\n"); + prdata(" --anta1 [BOOL] Antenna 1 available for A PHY\n"); + prdata(" --antbg0 [BOOL] Antenna 0 available for B/G PHY\n"); + prdata(" --antbg1 [BOOL] Antenna 1 available for B/G PHY\n"); + prdata(" --antga [0xFF] Antenna gain for A PHY\n"); + prdata(" --antgbg [0xFF] Antenna gain for B/G PHY\n"); + prdata(" --pa0b0 [0xFFFF]\n"); + prdata(" --pa0b1 [0xFFFF]\n"); + prdata(" --pa0b2 [0xFFFF]\n"); + prdata(" --pa1b0 [0xFFFF]\n"); + prdata(" --pa1b1 [0xFFFF]\n"); + prdata(" --pa1b2 [0xFFFF]\n"); + prdata(" --wl0gpio0 [0xFF] LED 0 behaviour\n"); + prdata(" --wl0gpio1 [0xFF] LED 1 behaviour\n"); + prdata(" --wl0gpio2 [0xFF] LED 2 behaviour\n"); + prdata(" --wl0gpio3 [0xFF] LED 3 behaviour\n"); + prdata(" --maxpa [0xFF] A PHY max power\n"); + prdata(" --maxpbg [0xFF] B/G PHY max power\n"); + prdata(" --itssia [0xFF] Idle tssi target for A PHY\n"); + prdata(" --itssibg [0xFF] Idle tssi target for B/G PHY\n"); + prdata(" --sver [0xFF] SPROM-version\n"); + prdata("\n"); + prdata(" -P|--print-all Display all values\n"); + prdata("\n"); + prdata(" BOOL is a boolean value. Either 0 or 1\n"); + prdata(" 0xF.. is a hexadecimal value\n"); + prdata(" MAC-ADDR is a MAC address in the format 00:00:00:00:00:00\n"); + prdata(" If the value parameter is \"GET\", the value will be printed;\n"); + prdata(" otherwise it is modified.\n"); +} + +#define ARG_MATCH 0 +#define ARG_NOMATCH 1 +#define ARG_ERROR -1 + +static int do_cmp_arg(char **argv, int *pos, + const char *template, + int allow_merged, + char **param) +{ + char *arg; + char *next_arg; + size_t arg_len, template_len; + + arg = argv[*pos]; + next_arg = argv[*pos + 1]; + arg_len = strlen(arg); + template_len = strlen(template); + + if (param) { + /* Maybe we have a merged parameter here. + * A merged parameter is "-pfoobar" for example. + */ + if (allow_merged && arg_len > template_len) { + if (memcmp(arg, template, template_len) == 0) { + *param = arg + template_len; + return ARG_MATCH; + } + return ARG_NOMATCH; + } else if (arg_len != template_len) + return ARG_NOMATCH; + *param = next_arg; + } + if (strcmp(arg, template) == 0) { + if (param) { + /* Skip the parameter on the next iteration. */ + (*pos)++; + if (*param == 0) { + prerror("%s needs a parameter\n", arg); + return ARG_ERROR; + } + } + return ARG_MATCH; + } + + return ARG_NOMATCH; +} + +/* Simple and lean command line argument parsing. */ +static int cmp_arg(char **argv, int *pos, + const char *long_template, + const char *short_template, + char **param) +{ + int err; + + if (long_template) { + err = do_cmp_arg(argv, pos, long_template, 0, param); + if (err == ARG_MATCH || err == ARG_ERROR) + return err; + } + err = ARG_NOMATCH; + if (short_template) + err = do_cmp_arg(argv, pos, short_template, 1, param); + return err; +} + +static int parse_err; + +static int arg_match(char **argv, int *i, + const char *long_template, + const char *short_template, + char **param) +{ + int res; + + res = cmp_arg(argv, i, long_template, + short_template, param); + if (res == ARG_ERROR) { + parse_err = 1; + return 0; + } + return (res == ARG_MATCH); +} + +static int parse_value(const char *str, + struct cmdline_vparm *vparm, + const char *param) +{ + unsigned long v; + int i; + + vparm->bits = value_length_map[vparm->type]; + vparm->set = 1; + if (strcmp(str, "GET") == 0 || strcmp(str, "get") == 0) { + vparm->set = 0; + return 0; + } + if (vparm->bits == 1) { + /* This is a boolean value. */ + if (strcmp(str, "0") == 0) + vparm->u.value = 0; + else if (strcmp(str, "1") == 0) + vparm->u.value = 1; + else + goto error_bool; + return 1; + } + + if (strncmp(str, "0x", 2) != 0) + goto error; + str += 2; + for (i = 0; i < vparm->bits / 4; i++) { + if (str[i] == '\0') + goto error; + } + if (str[i] != '\0') + goto error; + errno = 0; + v = strtoul(str, NULL, 16); + if (errno) + goto error; + vparm->u.value = v; + + return 1; +error: + if (param) { + prerror("%s value parsing error. Format: 0x", param); + for (i = 0; i < vparm->bits / 4; i++) + prerror("F"); + prerror("\n"); + } + return -1; + +error_bool: + if (param) + prerror("%s value parsing error. Format: 0 or 1 (boolean)\n", param); + return -1; +} + +static int parse_mac(const char *str, + struct cmdline_vparm *vparm, + const char *param) +{ + int i; + char *delim; + const char *in = str; + uint8_t *out = vparm->u.mac; + + vparm->bits = -1; + vparm->set = 1; + if (strcmp(str, "GET") == 0 || strcmp(str, "get") == 0) { + vparm->set = 0; + return 0; + } + + for (i = 0; ; i++) { + errno = 0; + out[i] = strtoul(in, NULL, 16); + if (errno) + goto error; + if (i == 5) { + if (in[1] != '\0' && in[2] != '\0') + goto error; + break; + } + delim = strchr(in, ':'); + if (!delim) + goto error; + in = delim + 1; + } + + return 1; +error: + prerror("%s MAC parsing error. Format: 00:00:00:00:00:00\n", param); + return -1; +} + +static int parse_rawset(const char *str, + struct cmdline_vparm *vparm) +{ + char *delim; + uint8_t value; + uint8_t offset; + int err; + + vparm->type = VALUE_RAW; + + delim = strchr(str, ','); + if (!delim) + goto error; + *delim = '\0'; + err = parse_value(str, vparm, NULL); + if (err != 1) + goto error; + offset = vparm->u.value; + if (offset >= SPROM_SIZE) { + prerror("--rawset offset too big (>= 0x%02X)\n", + SPROM_SIZE); + return -1; + } + err = parse_value(delim + 1, vparm, NULL); + if (err != 1) + goto error; + value = vparm->u.value; + + vparm->u.raw.value = value; + vparm->u.raw.offset = offset; + vparm->set = 1; + + return 0; +error: + prerror("--rawset value parsing error. Format: 0xFF,0xFF " + "(first Offset, second Value)\n"); + return -1; +} + +static int parse_rawget(const char *str, + struct cmdline_vparm *vparm) +{ + int err; + uint8_t offset; + + vparm->type = VALUE_RAW; + + err = parse_value(str, vparm, "--rawget"); + if (err != 1) + return -1; + offset = vparm->u.value; + if (offset >= SPROM_SIZE) { + prerror("--rawget offset too big (>= 0x%02X)\n", + SPROM_SIZE); + return -1; + } + + vparm->u.raw.offset = offset; + vparm->type = VALUE_RAW; + vparm->set = 0; + + return 0; +} + +static int generate_printall(void) +{ + struct cmdline_vparm *vparm; + int count, i; + enum valuetype vt = VALUE_FIRST; + + count = VALUE_LAST - VALUE_FIRST + 1; + for (i = 0; i < count; i++, vt++) { + if (cmdargs.nr_vparm == MAX_VPARM) { + prerror("Too many value parameters.\n"); + return -1; + } + + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = vt; + vparm->set = 0; + vparm->bits = value_length_map[vt]; + } + + return 0; +} + +static int parse_args(int argc, char *argv[]) +{ + struct cmdline_vparm *vparm; + int i, err; + char *param; + + parse_err = 0; + for (i = 1; i < argc; i++) { + if (cmdargs.nr_vparm == MAX_VPARM) { + prerror("Too many value parameters.\n"); + return -1; + } + + if (arg_match(argv, &i, "--version", "-v", 0)) { + print_banner(1); + return 1; + } else if (arg_match(argv, &i, "--help", "-h", 0)) { + goto out_usage; + } else if (arg_match(argv, &i, "--input", "-i", ¶m)) { + cmdargs.infile = param; + } else if (arg_match(argv, &i, "--output", "-o", ¶m)) { + cmdargs.outfile = param; + } else if (arg_match(argv, &i, "--verbose", "-V", 0)) { + cmdargs.verbose = 1; + } else if (arg_match(argv, &i, "--force", "-n", 0)) { + cmdargs.force = 1; + } else if (arg_match(argv, &i, "--binmode", "-b", 0)) { + cmdargs.bin_mode = 1; + + + } else if (arg_match(argv, &i, "--rawset", "-s", ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + err = parse_rawset(param, vparm); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--rawget", "-g", ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + err = parse_rawget(param, vparm); + if (err < 0) + goto error; + + + } else if (arg_match(argv, &i, "--subp", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_SUBP; + err = parse_value(param, vparm, "--subp"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--subv", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_SUBV; + err = parse_value(param, vparm, "--subv"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--ppid", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PPID; + err = parse_value(param, vparm, "--ppid"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--bflhi", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_BFLHI; + err = parse_value(param, vparm, "--bflhi"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--bfl", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_BFL; + err = parse_value(param, vparm, "--bfl"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--bgmac", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_BGMAC; + err = parse_mac(param, vparm, "--bgmac"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--etmac", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ETMAC; + err = parse_mac(param, vparm, "--etmac"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--amac", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_AMAC; + err = parse_mac(param, vparm, "--amac"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--et0phy", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ET0PHY; + err = parse_value(param, vparm, "--et0phy"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--et1phy", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ET1PHY; + err = parse_value(param, vparm, "--et1phy"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--et0mdc", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ET0MDC; + err = parse_value(param, vparm, "--et0mdc"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--et1mdc", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ET1MDC; + err = parse_value(param, vparm, "--et1mdc"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--brev", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_BREV; + err = parse_value(param, vparm, "--brev"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--loc", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_LOC; + err = parse_value(param, vparm, "--loc"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--anta0", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ANTA0; + err = parse_value(param, vparm, "--anta0"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--anta1", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ANTA1; + err = parse_value(param, vparm, "--anta1"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--antbg0", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ANTBG0; + err = parse_value(param, vparm, "--antbg0"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--antbg1", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ANTBG1; + err = parse_value(param, vparm, "--antbg1"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--antga", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ANTGA; + err = parse_value(param, vparm, "--antga"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--antgbg", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ANTGBG; + err = parse_value(param, vparm, "--antgbg"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--pa0b0", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PA0B0; + err = parse_value(param, vparm, "--pa0b0"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--pa0b1", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PA0B1; + err = parse_value(param, vparm, "--pa0b1"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--pa0b2", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PA0B2; + err = parse_value(param, vparm, "--pa0b2"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--pa1b0", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PA1B0; + err = parse_value(param, vparm, "--pa1b0"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--pa1b1", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PA1B1; + err = parse_value(param, vparm, "--pa1b1"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--pa1b2", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_PA1B2; + err = parse_value(param, vparm, "--pa1b2"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--wl0gpio0", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_WL0GPIO0; + err = parse_value(param, vparm, "--wl0gpio0"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--wl0gpio1", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_WL0GPIO1; + err = parse_value(param, vparm, "--wl0gpio1"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--wl0gpio2", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_WL0GPIO2; + err = parse_value(param, vparm, "--wl0gpio2"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--wl0gpio3", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_WL0GPIO3; + err = parse_value(param, vparm, "--wl0gpio3"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--maxpa", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_MAXPA; + err = parse_value(param, vparm, "--maxpa"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--maxpbg", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_MAXPBG; + err = parse_value(param, vparm, "--maxpbg"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--itssia", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ITSSIA; + err = parse_value(param, vparm, "--itssia"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--itssibg", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_ITSSIBG; + err = parse_value(param, vparm, "--itssibg"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--sver", 0, ¶m)) { + vparm = &(cmdargs.vparm[cmdargs.nr_vparm++]); + vparm->type = VALUE_SVER; + err = parse_value(param, vparm, "--sver"); + if (err < 0) + goto error; + } else if (arg_match(argv, &i, "--print-all", "-P", 0)) { + err = generate_printall(); + if (err) + goto error; + } else { + prerror("Unrecognized argument: %s\n", argv[i]); + goto out_usage; + } + if (parse_err) + goto out_usage; + } + if (cmdargs.nr_vparm == 0) { + prerror("No Value parameter given. See --help.\n"); + return -1; + } + return 0; + +out_usage: + print_usage(argc, argv); +error: + return -1; +} + + +int main(int argc, char **argv) +{ + int err; + int fd; + uint8_t sprom[SPROM_SIZE]; + char *buffer = NULL; + size_t buffer_size = 0; + + err = parse_args(argc, argv); + if (err == 1) + return 0; + else if (err != 0) + goto out; + + print_banner(0); + prinfo("\nReading input from \"%s\"...\n", + cmdargs.infile ? cmdargs.infile : "stdin"); + + err = open_infile(&fd); + if (err) + goto out; + err = read_infile(fd, &buffer, &buffer_size); + close_infile(fd); + if (err) + goto out; + err = parse_input(sprom, buffer, buffer_size); + free(buffer); + if (err) + goto out; + err = validate_input(sprom); + if (err) + goto out; + + err = display_sprom(sprom); + if (err) + goto out; + err = modify_sprom(sprom); + if (err < 0) + goto out; + if (err) { + err = open_outfile(&fd); + if (err) + goto out; + err = write_output(fd, sprom); + close_outfile(fd); + if (err) + goto out; + prinfo("SPROM modified.\n"); + } +out: + return err; +} diff --git a/ssb_sprom/ssb_sprom.h b/ssb_sprom/ssb_sprom.h new file mode 100644 index 0000000..37c97c8 --- /dev/null +++ b/ssb_sprom/ssb_sprom.h @@ -0,0 +1,126 @@ +/* + + Broadcom Sonics Silicon Backplane bus SPROM data modification tool + + Copyright (c) 2006-2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef SSB_SPROMTOOL_H_ +#define SSB_SPROMTOOL_H_ + +#include "utils.h" + +#define VERSION ssb_stringify(VERSION_) + +#define SPROM_SIZE 128 /* bytes */ + +/* byte offsets */ +#define SPROM_SUBP (0x02 * 2) +#define SPROM_SUBV (0x03 * 2) +#define SPROM_PPID (0x04 * 2) +#define SPROM_BFLHI (0x1C * 2) +#define SPROM_IL0MACADDR (0x24 * 2) +#define SPROM_ET0MACADDR (0x27 * 2) +#define SPROM_ET1MACADDR (0x2a * 2) +#define SPROM_ETHPHY (0x2d * 2) +#define SPROM_BOARDREV (0x2e * 2) +#define SPROM_PA0B0 (0x2f * 2) +#define SPROM_PA0B1 (0x30 * 2) +#define SPROM_PA0B2 (0x31 * 2) +#define SPROM_WL0GPIO0 (0x32 * 2) +#define SPROM_WL0GPIO2 (0x33 * 2) +#define SPROM_MAXPWR (0x34 * 2) +#define SPROM_PA1B0 (0x35 * 2) +#define SPROM_PA1B1 (0x36 * 2) +#define SPROM_PA1B2 (0x37 * 2) +#define SPROM_IDL_TSSI_TGT (0x38 * 2) +#define SPROM_BOARDFLAGS (0x39 * 2) +#define SPROM_ANTENNA_GAIN (0x3a * 2) +#define SPROM_VERSION (0x3f * 2) + +enum valuetype { + VALUE_RAW, + + VALUE_SUBP, + VALUE_FIRST = VALUE_SUBP, + VALUE_SUBV, + VALUE_PPID, + VALUE_BFLHI, + VALUE_BFL, + VALUE_BGMAC, + VALUE_ETMAC, + VALUE_AMAC, + VALUE_ET0PHY, + VALUE_ET1PHY, + VALUE_ET0MDC, + VALUE_ET1MDC, + VALUE_BREV, + VALUE_LOC, + VALUE_ANTA0, + VALUE_ANTA1, + VALUE_ANTBG0, + VALUE_ANTBG1, + VALUE_ANTGA, + VALUE_ANTGBG, + VALUE_PA0B0, + VALUE_PA0B1, + VALUE_PA0B2, + VALUE_PA1B0, + VALUE_PA1B1, + VALUE_PA1B2, + VALUE_WL0GPIO0, + VALUE_WL0GPIO1, + VALUE_WL0GPIO2, + VALUE_WL0GPIO3, + VALUE_MAXPA, + VALUE_MAXPBG, + VALUE_ITSSIA, + VALUE_ITSSIBG, + VALUE_SVER, + VALUE_LAST = VALUE_SVER, +}; + +struct cmdline_vparm { + enum valuetype type; + int set; + int bits; + union { + uint16_t value; + uint8_t mac[6]; + struct { + uint8_t value; + uint8_t offset; + } raw; + } u; +}; + +struct cmdline_args { + const char *infile; + const char *outfile; + int verbose; + int force; + int bin_mode; + +#define MAX_VPARM 512 + struct cmdline_vparm vparm[MAX_VPARM]; + int nr_vparm; +}; +extern struct cmdline_args cmdargs; + +#endif /* SSB_SPROMTOOL_H_ */ diff --git a/ssb_sprom/utils.c b/ssb_sprom/utils.c new file mode 100644 index 0000000..5d3189e --- /dev/null +++ b/ssb_sprom/utils.c @@ -0,0 +1,146 @@ +/* + + Broadcom Sonics Silicon Backplane bus SPROM data modification tool + + Copyright (c) 2006-2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "utils.h" +#include "ssb_sprom.h" + +#include +#include +#include +#include + + +int prinfo(const char *fmt, ...) +{ + int ret; + va_list va; + + if (!cmdargs.verbose) + return 0; + + va_start(va, fmt); + ret = vfprintf(stderr, fmt, va); + va_end(va); + + return ret; +} + +int prerror(const char *fmt, ...) +{ + int ret; + va_list va; + + va_start(va, fmt); + ret = vfprintf(stderr, fmt, va); + va_end(va); + + return ret; +} + +int prdata(const char *fmt, ...) +{ + int ret; + va_list va; + + va_start(va, fmt); + ret = vfprintf(stderr, fmt, va); + va_end(va); + + return ret; +} + +void internal_error(const char *message) +{ + prerror("Internal programming error: %s\n", message); + exit(1); +} + +static void oom(void) +{ + prerror("ERROR: Out of memory!\n" + "Virtual memory exhausted. " + "Please close some applications, " + "add more RAM or SWAP space.\n"); + exit(1); +} + +void * malloce(size_t size) +{ + void *ret; + + ret = malloc(size); + if (!ret) + oom(); + + return ret; +} + +void * realloce(void *ptr, size_t newsize) +{ + void *ret; + + ret = realloc(ptr, newsize); + if (!ret) + oom(); + + return ret; +} + +uint8_t crc8(uint8_t crc, uint8_t data) +{ + static const uint8_t t[] = { + 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, + 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, + 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, + 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, + 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, + 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, + 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, + 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, + 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, + 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, + 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, + 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, + 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, + 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, + 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, + 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, + 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, + 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, + 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, + 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, + 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, + 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, + 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, + 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, + 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, + 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, + 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, + 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, + 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, + 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, + 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, + 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, + }; + return t[crc ^ data]; +} diff --git a/ssb_sprom/utils.h b/ssb_sprom/utils.h new file mode 100644 index 0000000..bd60f98 --- /dev/null +++ b/ssb_sprom/utils.h @@ -0,0 +1,37 @@ +#ifndef SSB_SPROMTOOL_UTILS_H_ +#define SSB_SPROMTOOL_UTILS_H_ + +#include +#include +#include + +#define ssb_stringify_1(x) #x +#define ssb_stringify(x) ssb_stringify_1(x) + +#ifdef ATTRIBUTE_FORMAT +# undef ATTRIBUTE_FORMAT +#endif +#ifdef __GNUC__ +# define ATTRIBUTE_FORMAT(t, a, b) __attribute__((format(t, a, b))) +#else +# define ATTRIBUTE_FORMAT(t, a, b) /* nothing */ +#endif + +int prinfo(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +int prerror(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +int prdata(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); + +void internal_error(const char *message); +#define internal_error_on(condition) \ + do { \ + if (condition) \ + internal_error(ssb_stringify(condition)); \ + } while (0) + +void * malloce(size_t size); +void * realloce(void *ptr, size_t newsize); + +/* CRC-8 with polynomial x^8+x^7+x^6+x^4+x^2+1 */ +uint8_t crc8(uint8_t crc, uint8_t data); + +#endif /* SSB_SPROMTOOL_UTILS_H_ */