From 98db23750431fa650821f391aca2ce1898cb8020 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Mon, 19 May 2008 23:06:16 -0600 Subject: [PATCH 1/1] Imported Upstream version 0.4.2 --- AUTHORS | 15 + COPYING | 340 ++++++++++ INSTALL | 5 + Makefile | 78 +++ README | 112 +++ doc/ChangeLog | 165 +++++ doc/TODO | 1 + efibootmgr.spec | 36 + filelist-rpm | 3 + src/Makefile | 2 + src/efibootmgr/Makefile | 2 + src/efibootmgr/efibootmgr.c | 1052 +++++++++++++++++++++++++++++ src/efibootmgr/module.mk | 22 + src/include/crc32.h | 34 + src/include/disk.h | 90 +++ src/include/efi.h | 359 ++++++++++ src/include/efibootmgr.h | 57 ++ src/include/efichar.h | 32 + src/include/gpt.h | 177 +++++ src/include/list.h | 169 +++++ src/include/module.mk | 1 + src/include/scsi_ioctls.h | 45 ++ src/include/unparse_path.h | 35 + src/lib/Makefile | 2 + src/lib/crc32.c | 124 ++++ src/lib/disk.c | 502 ++++++++++++++ src/lib/efi.c | 692 +++++++++++++++++++ src/lib/efichar.c | 130 ++++ src/lib/gpt.c | 652 ++++++++++++++++++ src/lib/module.mk | 3 + src/lib/scsi_ioctls.c | 105 +++ src/lib/unparse_path.c | 550 +++++++++++++++ src/man/man8/efibootmgr.8 | 200 ++++++ src/man/man8/efibootmgr.8.docbook | 426 ++++++++++++ src/module.mk | 21 + tools/install.pl | 156 +++++ 36 files changed, 6395 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 doc/ChangeLog create mode 100644 doc/TODO create mode 100644 efibootmgr.spec create mode 100644 filelist-rpm create mode 100644 src/Makefile create mode 100644 src/efibootmgr/Makefile create mode 100644 src/efibootmgr/efibootmgr.c create mode 100644 src/efibootmgr/module.mk create mode 100644 src/include/crc32.h create mode 100644 src/include/disk.h create mode 100644 src/include/efi.h create mode 100644 src/include/efibootmgr.h create mode 100644 src/include/efichar.h create mode 100644 src/include/gpt.h create mode 100644 src/include/list.h create mode 100644 src/include/module.mk create mode 100644 src/include/scsi_ioctls.h create mode 100644 src/include/unparse_path.h create mode 100644 src/lib/Makefile create mode 100644 src/lib/crc32.c create mode 100644 src/lib/disk.c create mode 100644 src/lib/efi.c create mode 100644 src/lib/efichar.c create mode 100644 src/lib/gpt.c create mode 100644 src/lib/module.mk create mode 100644 src/lib/scsi_ioctls.c create mode 100644 src/lib/unparse_path.c create mode 100644 src/man/man8/efibootmgr.8 create mode 100644 src/man/man8/efibootmgr.8.docbook create mode 100644 src/module.mk create mode 100755 tools/install.pl diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3059aa5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,15 @@ +Matt Domsch +- All .c and .h files + + +Andreas Schwab +- Patches to several .c and .h files + +Richard Hirst +- Patch to efichar.c + +dann frazier +- docbook of manpage +- Patches to efibootmgr.c +- network boot entry creation in efi.c + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy 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/INSTALL b/INSTALL new file mode 100644 index 0000000..d61b0be --- /dev/null +++ b/INSTALL @@ -0,0 +1,5 @@ +Running 'make' builds the file src/efibootmgr/efibootmgr. +efibootmgr should be placed into /usr/sbin/. + + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..67d275a --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ + default: all + + RELEASE_DATE := "04-Sep-2003" + RELEASE_MAJOR := 0 + RELEASE_MINOR := 4 + RELEASE_SUBLEVEL := 2 + RELEASE_EXTRALEVEL := + RELEASE_NAME := efibootmgr + RELEASE_STRING := $(RELEASE_NAME)-$(RELEASE_MAJOR).$(RELEASE_MINOR).$(RELEASE_SUBLEVEL)$(RELEASE_EXTRALEVEL) + + CFLAGS += -DEFIBOOTMGR_VERSION=\"$(RELEASE_MAJOR).$(RELEASE_MINOR).$(RELEASE_SUBLEVEL)$(RELEASE_EXTRALEVEL)\" -Wall + + MODULES := src + + BINDIR := /usr/sbin + +#-------------------------------------------- +# Generic Makefile stuff is below. You +# should not have to modify any of the stuff +# below. +#-------------------------------------------- + +#Included makefiles will add their deps for each stage in these vars: + INSTALLDEPS := + CLEANDEPS := + ALLDEPS := + CLEANLIST := + +#Define the top-level build directory + BUILDDIR := $(shell pwd) + +#Include make rules from each submodule (subdirectory) + include $(patsubst %,%/module.mk,$(MODULES)) + + .PHONY: all clean install_list install install_link post_install tarball echotree default + + all: $(ALLDEPS) + clean: clean_list $(CLEANDEPS) + + clean_list: + rm -f $(CLEANLIST) + + install_list: echotree $(INSTALLDEPS) + + install: all + @make install_list | tools/install.pl copy + + install_link: all + @make install_list | tools/install.pl link + + post_install: + + tarball: clean + -rm $(RELEASE_NAME)*.tar.gz + cp -a ../$(RELEASE_NAME) ../$(RELEASE_STRING) + find ../$(RELEASE_STRING) -name CVS -type d -depth -exec rm -rf \{\} \; + sync; sync; sync; + cd ..; tar cvzf $(RELEASE_STRING).tar.gz $(RELEASE_STRING) + mv ../$(RELEASE_STRING).tar.gz . + rm -rf ../$(RELEASE_STRING) + + +#The rest of the docs... + doc_TARGETS += COPYING README INSTALL + +echotree: + @# making directory tree + @#RPM FORMAT: + @# %defattr(-, user, group) + @# %attr(4755,user,group) filename + @# filename + +# Here is a list of variables that are assumed Local to each Makefile. You can +# safely stomp on these values without affecting the build. +# MODULES +# FILES +# TARGETS +# SOURCES diff --git a/README b/README new file mode 100644 index 0000000..1fb38fd --- /dev/null +++ b/README @@ -0,0 +1,112 @@ +This is efibootmgr, a Linux user-space application to modify the Intel +Extensible Firmware Interface (EFI) Boot Manager. This application +can create and destroy boot entries, change the boot order, change +the next running boot option, and more. + +Details on the EFI Boot Manager are available from the EFI +Specification, v1.02 or above, available from http://developer.intel.com. + +Note: efibootmgr requires that the kernel module efivars be loaded +prior to use. 'modprobe efivars' should do the trick. + +usage: efibootmgr [options] + -a | --active sets bootnum active + -A | --inactive sets bootnum inactive + -b | --bootnum XXXX modify BootXXXX (hex) + -B | --delete-bootnum delete bootnum (hex) + -c | --create create new variable bootnum and add to bootorder + -d | --disk disk (defaults to /dev/sda) containing loader + -e | --edd [1|3|-1] force EDD 1.0 or 3.0 creation variables, or guess + -E | --device num EDD 1.0 device number (defaults to 0x80) + -g | --gpt force disk w/ invalid PMBR to be treated as GPT + -H | --acpi_hid XXXX set the ACPI HID (used with -i) + -i | --iface name create a netboot entry for the named interface + -l | --loader name (defaults to \elilo.efi) + -L | --label label Boot manager display label (defaults to "Linux") + -n | --bootnext XXXX set BootNext to XXXX (hex) + -N | --delete-bootnext delete BootNext + -o | --bootorder XXXX,YYYY,ZZZZ,... explicitly set BootOrder (hex) + -O | --delete-bootorder delete BootOrder + -p | --part part (defaults to 1) containing loader + -q | --quiet be quiet + -t | --test filename don't write to NVRAM, write to filename + -u | --unicode | --UCS-2 pass extra args as UCS-2 (default is ASCII) + -U | --acpi_uid XXXX set the ACPI UID (used with -i) + -v | --verbose print additional information + -V | --version return version and exit + -w | --write-signature write unique sig to MBR if needed + + +Typical usage: +1) Root can use it to display the current Boot Manager settings. + [root@localhost ~]# efibootmgr + BootCurrent: 0004 + BootNext: 0003 + BootOrder: 0004,0000,0001,0002,0003 + Boot0000* Diskette Drive(device:0) + Boot0001* CD-ROM Drive(device:FF) + Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) + Boot0003* PXE Boot: MAC(00D0B7C15D91) + Boot0004* Linux + + This shows: + BootCurrent - the boot entry used to start the currently running + system. + + BootOrder - the boot order as would appear in the boot manager. The + boot manager tries to boot the first active entry on this list. If + unsuccessful, it tries the next entry, and so on. + + BootNext - the boot entry which is scheduled to be run on next boot. + This superceeds BootOrder for one boot only, and is deleted by the + boot manager after first use. This allows you to change the next boot + behavior without changing BootOrder. + + Five boot entries (0000 - 0004), the active/inactive flag (* means + active), and the name displayed on the screen. + + +2) An OS installer would call 'efibootmgr -c'. This assumes that + /boot/efi is your EFI System Partition, and is mounted at /dev/sda1. + This creates a new boot option, called "Linux", and puts it at the top + of the boot order list. Options may be passed to modify the + default behavior. The default OS Loader is elilo.efi. + +3) A system administrator wants to change the boot order. She would + call 'efibootmgr -o 3,4' to specify PXE boot first, then Linux + boot. + +4) A system administrator wants to change the boot order for the next + boot only. She would call 'efibootmgr -n 4' to specify that the + Linux entry be taken on next boot. + +5) A system administrator wants to delete the Linux boot option from + the menu. 'efibootmgr -b 4 -B' deletes entry 4 and removes it + from BootOrder. + +6) A system administrator wants to create a boot option to network + boot (PXE). Unfortunately, this requires knowing a little more + information about your system than can be easily found by + efibootmgr, so you've got to pass additional information - the ACPI + HID and UID values. These can generally be found by using the EFI + Boot Manager (in the EFI environment) to create a network boot + entry, then using efibootmgr to print it verbosely. Here's one example: + + Boot003* Acpi(PNP0A03,0)/PCI(5|0)/Mac(00D0B7F9F510) \ + ACPI(a0341d0,0)PCI(0,5)MAC(00d0b7f9f510,0) + + In this case, the ACPI HID is "0A0341d0" and the UID is "0". + For the zx2000 gigE, the HID is "222F" and the UID is "500". + For the rx2000 gigE, the HID is "0002" and the UID is "100". + You create the boot entry with: + 'efibootmgr -c -i eth0 -H 222F -U 500 -L netboot' + +Many other uses may be found. + +Please direct any bugs, features, patches, etc. to Matt Domsch +. + + + + + diff --git a/doc/ChangeLog b/doc/ChangeLog new file mode 100644 index 0000000..5daad8f --- /dev/null +++ b/doc/ChangeLog @@ -0,0 +1,165 @@ +* Thu Sep 04 2003 Matt Domsch +- released v0.4.2-test2 as v0.4.2 without additional changes. + +* Mon Aug 11 2003 Matt Domsch +- fixed unaligned access errors +- removed extraneous printing of mac addr when creating netboot entries +- sync docbook to README +- whitespace cleanups + +* Thu Jul 31 2003 Matt Domsch +- Applied patch from Dann Frazier to enable creating netboot entries. +- update AUTHORS with Dann's netboot contribution. +- Until we can get the ACPI HID and UID programatically, make the user + pass them in when creating a netboot entry. +- Add O_DIRECT support for reading the disk. +- Fix unparse_hardware_path() for the PCI case - the device and function + values were printed in reverse order. +- Fix the README file to reflect all the options that can be passed, and + add a new item for netboot entries. +- whitespace cleanups + +* Fri Oct 25 2002 Matt Domsch +- trivial patch from Fabien Lebaillif - Delamare + increases the length of the boot option description from 40 to 64 bytes. + +* Tue Oct 22 2002 Matt Domsch +- ran docbook2man on the man page Dann Frazier created +- Added man page to spec file +- Added patches from Dann Frazier + - Removes the requirement to be root to run efibootmgr. This lets + mortal users run efibootmgr to check the version, and see the + --help output. It should also allow efibootmgr to work under + security systems without the strict root/non-root dichotomy. + - Checks to see if a boot option was specified when a boot option + deletion was requested. +- Released version 0.4.1 + +* Sun Jul 21 2002 Matt Domsch +- Added kernel_has_blkgetsize64() test, now uses BLKGETSIZE64 if on a + known-good kernel. This is important when the Large Block Device (64-bit + block address) patch gets merged into the 2.5 kernel. + +* Wed May 1 2002 Matt Domsch +- Released version 0.4.0 + +* Tue Apr 30 2002 Matt Domsch +- Added some printfs to compare_gpts(). + +* Mon Apr 08 2002 Matt Domsch +- Make sure partition number arg is in the GPT disk. +- Added a few more _FILE_OFFSET_BITS defines where needed + +* Fri Apr 05 2002 Matt Domsch +- Added syncs to make tarball target so NFS is current and CVS dirs get + deleted before the tarball is made. +- Added a few __leXX_to_cpu() calls in code currently commented out in gpt.c + +* Tue Apr 02 2002 Matt Domsch +- finished conversion to GPT code + +* Sat Mar 30 2002 Matt Domsch +- began conversion to GPT code in post-2.4.18 and post-2.5.7 kernels. +- added manpage contributed by dannf@fc.hp.com + +* Mon Feb 11 2002 Matt Domsch +- BLKGETSIZE64 is fixed in 2.4.18-pre8 and 2.5.4-pre3. Wait a bit longer +before using though. +- added list_for_each_safe() routine. It's needed in the boot +var deletion routine, though it was already breaking out once found. +- changed struct list_head to list_t +- released v0.4.0-test4 + +* Fri Jan 18 2002 Matt Domsch +- commented out BLKGETSIZE64 ioctl use for now. Kernels 2.4.15-2.4.18 and + 2.5.0-2.5.3 don't return the right value. + +* Thu Jan 3 2002 Matt Domsch +- added back in read last sector IOCTL hack, but only after read() fails. +- released v0.4.0-test4 + +* Thu Jan 3 2002 Matt Domsch +- more cleanups +- released v0.4.0-test3 + +* Wed Jan 2 2002 Matt Domsch +- Changed PROC_DIR_EFI to PROC_DIR_EFI_VARS +- write_variable() now searches /proc/efi/vars for a variable that's not + the one being written. The EFI 1.1 sample implementation changed the + name of the "victim" being written to (was Efi-xxxxx, now EFI-xxxxx), so + previous versions of efibootmgr don't work with the new firmware. This + should fix that up. +- released v0.4.0-test2 + +* Fri Dec 7 2001 Matt Domsch +- Removed read last sector ioctl hack, it's not needed anymore. The + kernel takes care of it for us with a new patch. +- Added test for valid PMBR, similar to parted and the kernel. +- Added test for returning size of block device as a u64. +- Added test for returning sector size as int, and use that. +- Changed GPT struct and member names to be more Linux-like. +- added -g option to force disk with invalid PMBR to be treated as GPT anyhow. +- released v0.4.0-test1 + +* Thu Aug 9 2001 Matt Domsch +- Added some uniqueness to the MBR signature. +- removed ExclusiveArch from spec file +- released v0.3.4 + +* Mon Aug 6 2001 Matt Domsch +- applied patch from Richard Hirst to fix + efichar_char_strcmp(). + + +* Fri Aug 3 2001 Matt Domsch +- By request, warn when creating a new boot entry with the same name + as another boot entry. +- released v0.3.3 + +* Mon Jul 30 2001 Matt Domsch +- Added test for non-zero signature on MBR-style disks, + and new -w flag to write a unique signature to the disk if so. +- released v0.3.3-test4 +- Fixed counting of optional data length (extra args) +- released v0.3.3-test5 + +* Fri Jul 27 2001 Matt Domsch +- Added test for running as root +- released v0.3.3-test3 + +* Thu Jul 26 2001 Matt Domsch +- Unparse extra args being passed to bootloader too. +- released v0.3.3-test2 + +* Wed Jul 25 2001 Matt Domsch +- Added -u option to pass extra args to the bootloader in unicode + (default is to pass in ASCII) +- Added -t option to allow writing to a file (for testing) +- released v0.3.3-test1 + +* Tue May 22 2001 Matt Domsch +- Applied patch from Andreas Schwab. remove_from_boot_order() called + read_boot_order() without checking the return status. +- Changed README to remove os loader args comment +- Changed efi_status_t to be unsigned long (not uint64_t) as per EFI + spec. This allows efibootmgr to be recompiled as a 32-bit or 64-bit app + depending on its running on 32-bit or 64-bit Linux. Note: this changes + the size of the structure returned in /proc/efi/vars to match the kernel + implementation. 32-bit efibootmgr won't work on 64-bit Linux and vice versa. +- release v0.3.2 + +* Fri May 18 2001 Matt Domsch +- changed usage() to *not* let you think you can pass args to the OS + loader +- release v0.3.1 + +* Fri May 18 2001 Matt Domsch +- Padded HARDDRIVE_DEVICE_PATH out to please EFI Boot Manager. +- Incorporated patches from Andreas Schwab + - replace __u{8,16,32,64} with uint{8,16,32,64}_t + - use _FILE_OFFSET_BITS + - fix a segfault +- release v0.3.0 + +* Tue May 15 2001 Matt Domsch +- initial external release v0.2.0 diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 0000000..0c8d016 --- /dev/null +++ b/doc/TODO @@ -0,0 +1 @@ +* MS-DOS style extended partitions diff --git a/efibootmgr.spec b/efibootmgr.spec new file mode 100644 index 0000000..45627e9 --- /dev/null +++ b/efibootmgr.spec @@ -0,0 +1,36 @@ +Summary: EFI Boot Manager +Name: efibootmgr +Version: 0.4.1 +Release: 0 +Group: System Environment/Base +Copyright: GPL +Vendor: Dell Computer Corporation www.dell.com +Packager: Matt Domsch + + +Source0: http://domsch.com/linux/ia64/efibootmgr/efibootmgr-%{version}.tar.gz + +%description +efibootmgr displays and allows the user to edit the Intel Extensible +Firmware Interface (EFI) Boot Manager variables. Additional +information about EFI can be found at +http://developer.intel.com/technology/efi/efi.htm. + +%prep +%setup +%build +make +%install +install --group=root --owner=root --mode 555 src/efibootmgr/efibootmgr $RPM_BUILD_ROOT/usr/sbin +install --group=root --owner=root --mode 444 src/man/man8/efibootmgr.8 $RPM_BUILD_ROOT/usr/share/man/man8 + +%files +/usr/sbin/efibootmgr +/usr/share/man/man8/efibootmgr.8 +%doc README +%doc INSTALL + + +%changelog +* Fri May 18 2001 Matt Domsch +- See doc/ChangeLog diff --git a/filelist-rpm b/filelist-rpm new file mode 100644 index 0000000..07cd431 --- /dev/null +++ b/filelist-rpm @@ -0,0 +1,3 @@ + %attr(0755,root,root) /usr/sbin + %attr(0755,root,root) /usr/sbin/efibootmgr + %attr(0444,root,root) /usr/share/man/man8/efibootmgr.8 diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..6615d46 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,2 @@ +%: + +make -C ../ $* diff --git a/src/efibootmgr/Makefile b/src/efibootmgr/Makefile new file mode 100644 index 0000000..6615d46 --- /dev/null +++ b/src/efibootmgr/Makefile @@ -0,0 +1,2 @@ +%: + +make -C ../ $* diff --git a/src/efibootmgr/efibootmgr.c b/src/efibootmgr/efibootmgr.c new file mode 100644 index 0000000..4184143 --- /dev/null +++ b/src/efibootmgr/efibootmgr.c @@ -0,0 +1,1052 @@ +/* + efibootmgr.c - Manipulates EFI variables as exported in /proc/efi/vars + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + This must tie the EFI_DEVICE_PATH to /boot/efi/elilo.efi + The EFI_DEVICE_PATH will look something like: + ACPI device path, length 12 bytes + Hardware Device Path, PCI, length 6 bytes + Messaging Device Path, SCSI, length 8 bytes, or ATAPI, length ?? + Media Device Path, Hard Drive, partition XX, length 30 bytes + Media Device Path, File Path, length ?? + End of Hardware Device Path, length 4 + Arguments passed to elilo, as UCS-2 characters, length ?? + +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "efi.h" +#include "efichar.h" +#include "unparse_path.h" +#include "disk.h" +#include "efibootmgr.h" + +#ifndef EFIBOOTMGR_VERSION +#define EFIBOOTMGR_VERSION "unknown (fix Makefile!)" +#endif + + +typedef struct _var_entry { + struct dirent *name; + uint16_t num; + efi_variable_t var_data; + list_t list; +} var_entry_t; + + +/* global variables */ +static LIST_HEAD(boot_entry_list); +static LIST_HEAD(blk_list); +efibootmgr_opt_t opts; + +static inline void +var_num_from_name(const char *pattern, char *name, uint16_t *num) +{ + sscanf(name, pattern, num); +} + +static int +select_boot_var_names(const struct dirent *d) +{ + int num, rc; + rc = sscanf(d->d_name, "Boot0%03x-%*s", &num); + return rc; +} + +#if 0 +static int +select_blk_var_names(const struct dirent *d) +{ + int num; + return sscanf(d->d_name, "blk%x-%*s", &num); +} +#endif + +static int +read_boot_var_names(struct dirent ***namelist) +{ + int n; + n = scandir(PROC_DIR_EFI_VARS, namelist, select_boot_var_names, alphasort); + if (n < 0) { + perror("scandir " PROC_DIR_EFI_VARS); + fprintf(stderr, "You must 'modprobe efivars' before running efibootmgr.\n"); + } + return n; +} + +#if 0 +static int +read_blk_var_names(struct dirent ***namelist) +{ + int n; + n = scandir(PROC_DIR_EFI_VARS, namelist, select_blk_var_names, alphasort); + if (n < 0) + perror("scandir"); + return n; +} + +static int +dirent_list_length(struct dirent **namelist) +{ + int i; + if (!namelist) return 0; + for (i=0; namelist[i]; i++); + return i; +} +#endif + +static void +read_vars(struct dirent **namelist, + int num_boot_names, + list_t *head) +{ + efi_status_t status; + var_entry_t *entry; + int i; + + if (!namelist) return; + + for (i=0; i < num_boot_names; i++) + { + if (namelist[i]) { + entry = malloc(sizeof(var_entry_t)); + if (!entry) return; + memset(entry, 0, sizeof(var_entry_t)); + + status = read_variable(namelist[i]->d_name, + &entry->var_data); + if (status != EFI_SUCCESS) break; + entry->name = namelist[i]; + list_add_tail(&entry->list, head); + } + } + return; +} + + + + + +static void +free_dirents(struct dirent **ptr, int num_dirents) +{ + int i; + if (!ptr) return; + for (i=0; i < num_dirents; i++) { + if (ptr[i]) { + free(ptr[i]); + ptr[i] = NULL; + } + } + free(ptr); +} + + + +static int +compare(const void *a, const void *b) +{ + int rc = -1; + uint32_t n1, n2; + memcpy(&n1, a, sizeof(n1)); + memcpy(&n2, b, sizeof(n2)); + if (n1 < n2) rc = -1; + if (n1 == n2) rc = 0; + if (n2 > n2) rc = 1; + return rc; +} + + +/* + Return an available boot variable number, + or -1 on failure. +*/ +static int +find_free_boot_var(list_t *boot_list) +{ + int num_vars=0, i=0, found; + uint16_t *vars, free_number; + list_t *pos; + var_entry_t *boot; + list_for_each(pos, boot_list) { + num_vars++; + } + vars = malloc(sizeof(uint16_t) * num_vars); + if (!vars) return -1; + memset(vars, 0, sizeof(uint16_t) * num_vars); + + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + vars[i] = boot->num; + i++; + } + qsort(vars, i, sizeof(uint16_t), compare); + found = 1; + + num_vars = i; + for (free_number = 0; free_number < num_vars && found; free_number++) { + found = 0; + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + if (boot->num == free_number) { + found = 1; + break; + } + } + if (!found) break; + } + if (found && num_vars) free_number = vars[num_vars-1] + 1; + free(vars); + return free_number; +} + + +static void +warn_duplicate_name(list_t *boot_list) +{ + list_t *pos; + var_entry_t *boot; + EFI_LOAD_OPTION *load_option; + + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + load_option = (EFI_LOAD_OPTION *) + boot->var_data.Data; + if (!efichar_char_strcmp(opts.label, + load_option->description)) { + fprintf(stderr, "** Warning ** : Boot%04x has same label %s\n", + boot->num, + opts.label); + } + } +} + + +static var_entry_t * +make_boot_var(list_t *boot_list) +{ + var_entry_t *boot; + int free_number; + + if (opts.bootnum == -1) free_number = find_free_boot_var(boot_list); + else free_number = opts.bootnum; + + if (free_number == -1) return NULL; + + /* Create a new var_entry_t object + and populate it. + */ + + boot = malloc(sizeof(*boot)); + if (!boot) return NULL; + memset(boot, 0, sizeof(*boot)); + boot->num = free_number; + if (!make_linux_efi_variable(&boot->var_data, free_number)) { + free(boot); + return NULL; + } + write_variable(&boot->var_data); + list_add_tail(&boot->list, boot_list); + return boot; +} + +static efi_status_t +read_boot_order(efi_variable_t *boot_order) +{ + efi_status_t status; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char boot_order_name[80], text_uuid[40]; + efi_guid_unparse(&guid, text_uuid); + + memset(boot_order, 0, sizeof(*boot_order)); + sprintf(boot_order_name, "BootOrder-%s", text_uuid); + + status = read_variable(boot_order_name, boot_order); + if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) + return status; + + if (status == EFI_NOT_FOUND) { + /* Create it */ + efichar_from_char(boot_order->VariableName, "BootOrder", + 1024); + memcpy(&boot_order->VendorGuid, &guid, sizeof(guid)); + boot_order->Attributes = EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS; + return status; + } + return EFI_SUCCESS; +} + + + + +static efi_status_t +add_to_boot_order(uint16_t num) +{ + efi_status_t status; + efi_variable_t boot_order; + uint64_t new_data_size; + uint16_t *new_data, *old_data; + + status = read_boot_order(&boot_order); + if (status != EFI_SUCCESS) return status; + + /* We've now got an array (in boot_order.Data) of the + boot order. First add our entry, then copy the old array. + */ + old_data = (uint16_t *)&(boot_order.Data); + new_data_size = boot_order.DataSize + sizeof(uint16_t); + new_data = malloc(new_data_size); + + new_data[0] = num; + memcpy(new_data+1, old_data, boot_order.DataSize); + + /* Now new_data has what we need */ + memcpy(&(boot_order.Data), new_data, new_data_size); + boot_order.DataSize = new_data_size; + return write_variable(&boot_order); +} + + +static efi_status_t +remove_from_boot_order(uint16_t num) +{ + efi_status_t status; + efi_variable_t boot_order; + uint64_t new_data_size; + uint16_t *new_data, *old_data; + int old_i,new_i; + + status = read_boot_order(&boot_order); + if (status != EFI_SUCCESS) return status; + + /* If it's empty, yea! */ + if (!boot_order.DataSize) return EFI_SUCCESS; + + /* We've now got an array (in boot_order.Data) of the + boot order. Simply copy the array, skipping the + entry we're deleting. + */ + old_data = (uint16_t *)&(boot_order.Data); + /* Start with the same size */ + new_data_size = boot_order.DataSize; + new_data = malloc(new_data_size); + for (old_i=0,new_i=0; + old_i < boot_order.DataSize / sizeof(uint16_t); + old_i++) { + if (old_data[old_i] != num) { + /* Copy this value */ + new_data[new_i] = old_data[old_i]; + new_i++; + } + } + + /* Now new_data has what we need */ + new_data_size = new_i * sizeof(uint16_t); + memset(&(boot_order.Data), 0, boot_order.DataSize); + memcpy(&(boot_order.Data), new_data, new_data_size); + boot_order.DataSize = new_data_size; + + return write_variable(&boot_order); +} + +static int +read_boot_current() +{ + efi_status_t status; + efi_variable_t boot_next; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char boot_next_name[80], text_uuid[40]; + uint16_t *n = (uint16_t *)(boot_next.Data); + + efi_guid_unparse(&guid, text_uuid); + + memset(&boot_next, 0, sizeof(boot_next)); + sprintf(boot_next_name, "BootCurrent-%s", text_uuid); + + status = read_variable(boot_next_name, &boot_next); + if (status) return -1; + + return *n; +} + +static int +read_boot_next() +{ + efi_status_t status; + efi_variable_t boot_next; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char boot_next_name[80], text_uuid[40]; + uint16_t *n = (uint16_t *)(boot_next.Data); + + efi_guid_unparse(&guid, text_uuid); + + memset(&boot_next, 0, sizeof(boot_next)); + sprintf(boot_next_name, "BootNext-%s", text_uuid); + + status = read_variable(boot_next_name, &boot_next); + if (status) return -1; + + return *n; +} + + +static efi_status_t +set_boot_next(uint16_t num) +{ + efi_variable_t var; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + uint16_t *n = (uint16_t *)var.Data; + + memset(&var, 0, sizeof(var)); + + efichar_from_char(var.VariableName, "BootNext", + 1024); + memcpy(&var.VendorGuid, &guid, sizeof(guid)); + *n = num; + var.DataSize = sizeof(uint16_t); + var.Attributes = EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS; + return write_variable(&var); +} + +static efi_status_t +delete_boot_next() +{ + efi_variable_t var; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + + memset(&var, 0, sizeof(var)); + + efichar_from_char(var.VariableName, "BootNext", + 1024); + memcpy(&var.VendorGuid, &guid, sizeof(guid)); + return write_variable(&var); +} + + +static efi_status_t +delete_boot_var(uint16_t num) +{ + efi_status_t status; + efi_variable_t var; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char name[80]; + list_t *pos, *n; + var_entry_t *boot; + sprintf(name, "Boot%04x", num); + + memset(&var, 0, sizeof(var)); + + efichar_from_char(var.VariableName, name, 1024); + memcpy(&var.VendorGuid, &guid, sizeof(guid)); + status = write_variable(&var); + + if (status) return status; + + list_for_each_safe(pos, n, &boot_entry_list) { + boot = list_entry(pos, var_entry_t, list); + if (boot->num == num) { + status = remove_from_boot_order(num); + if (status) return status; + list_del(&(boot->list)); + break; /* short-circuit since it was found */ + } + } + return EFI_SUCCESS; +} + + +static void +set_var_nums(const char *pattern, list_t *list) +{ + list_t *pos; + var_entry_t *var; + int num=0, rc; + + list_for_each(pos, list) { + var = list_entry(pos, var_entry_t, list); + rc = sscanf(var->name->d_name, pattern, &num); + if (rc == 1) var->num = num; + } +} + +#if 0 +static efi_variable_t * +find_pci_scsi_disk_blk(int fd, int bus, int device, int func, + list_t *blk_list) +{ + list_t *pos; + int rc; + Scsi_Idlun idlun; + unsigned char host, channel, id, lun; + var_entry_t *blk; + efi_variable_t *blk_var; + long size = 0; + + memset(&idlun, 0, sizeof(idlun)); + rc = get_scsi_idlun(fd, &idlun); + if (rc) return NULL; + + rc = disk_get_size(fd, &size); + + idlun_to_components(&idlun, &host, &channel, &id, &lun); + + list_for_each(pos, blk_list) { + blk = list_entry(pos, var_entry_t, list); + blk_var = blk->var_data; + + if (!compare_pci_scsi_disk_blk(blk_var, + bus, device, func, + host, channel, id, lun, + 0, size)) { + return blk_var; + } + } + return NULL; +} + + + + +/* The right blkX variable contains: + 1) the PCI and SCSI information for the disk passed in disk_name + 2) Does not contain a partition field +*/ + + +static efi_variable_t * +find_disk_blk(char *disk_name, list_t *blk_list) +{ + efi_variable_t *disk_blk = NULL; + int fd, rc; + unsigned char bus=0,device=0,func=0; + int interface_type=interface_type_unknown; + unsigned int controllernum=0, disknum=0; + unsigned char part=0; + + fd = open(disk_name, O_RDONLY|O_DIRECT); + rc = disk_get_pci(fd, &bus, &device, &func); + if (rc) { + fprintf(stderr, "disk_get_pci() failed.\n"); + return NULL; + } + rc = disk_info_from_fd(fd, + &interface_type, + &controllernum, + &disknum, + &part); + if (rc) { + fprintf(stderr, "disk_info_from_fd() failed.\n"); + return NULL; + } + switch (interface_type) + { + case scsi: + return find_pci_scsi_disk_blk(fd,bus,device,func,blk_list); + break; + case ata: + return find_pci_ata_disk_blk(fd,bus,device,func,blk_list); + break; + case i2o: + return find_pci_i2o_disk_blk(fd,bus,device,func,blk_list); + break; + case md: + return find_pci_md_disk_blk(fd,bus,device,func,blk_list); + break; + default: + break; + } + return NULL; +} +#endif + +static void +unparse_boot_order(uint16_t *order, int length) +{ + int i; + printf("BootOrder: "); + for (i=0; ivar_data.Data; + efichar_to_char(description, + load_option->description, sizeof(description)); + memset(text_path, 0, sizeof(text_path)); + path = load_option_path(load_option); + printf("Boot%04x", boot->num); + if (load_option->attributes & LOAD_OPTION_ACTIVE) + printf("* "); + else printf(" "); + printf("%s", description); + + if (opts.verbose) { + unparse_path(text_path, path, + load_option->file_path_list_length); + /* Print optional data */ + optional_data_len = + boot->var_data.DataSize - + load_option->file_path_list_length - + ((char *)path - (char *)load_option); + if (optional_data_len) { + p = text_path; + p += strlen(text_path); + unparse_raw_text(p, ((uint8_t *)path) + + load_option->file_path_list_length, + optional_data_len); + } + + printf("\t%s", text_path); + } + printf("\n"); + } +} + + + +static void +show_boot_order() +{ + efi_status_t status; + efi_variable_t boot_order; + uint16_t *data; + + status = read_boot_order(&boot_order); + + if (status != EFI_SUCCESS) { + perror("show_boot_order()"); + return; + } + + /* We've now got an array (in boot_order.Data) of the + boot order. First add our entry, then copy the old array. + */ + data = (uint16_t *)&(boot_order.Data); + if (boot_order.DataSize) + unparse_boot_order(data, boot_order.DataSize / sizeof(uint16_t)); + +} + +static efi_status_t +set_active_state() +{ + list_t *pos; + var_entry_t *boot; + EFI_LOAD_OPTION *load_option; + + list_for_each(pos, &boot_entry_list) { + boot = list_entry(pos, var_entry_t, list); + load_option = (EFI_LOAD_OPTION *) + boot->var_data.Data; + if (boot->num == opts.bootnum) { + if (opts.active == 1) { + if (load_option->attributes + & LOAD_OPTION_ACTIVE) return EFI_SUCCESS; + else { + load_option->attributes + |= LOAD_OPTION_ACTIVE; + return write_variable(&boot->var_data); + } + } + else if (opts.active == 0) { + if (!(load_option->attributes + & LOAD_OPTION_ACTIVE)) + return EFI_SUCCESS; + else { + load_option->attributes + &= ~LOAD_OPTION_ACTIVE; + return write_variable(&boot->var_data); + } + } + } + } + return EFI_SUCCESS; +} + + + +static efi_status_t +delete_boot_order() +{ + efi_variable_t var; + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + + memset(&var, 0, sizeof(var)); + + efichar_from_char(var.VariableName, "BootOrder", + 1024); + memcpy(&var.VendorGuid, &guid, sizeof(guid)); + return write_variable(&var); +} + + +static void +usage() +{ + printf("efibootmgr version %s\n", EFIBOOTMGR_VERSION); + printf("usage: efibootmgr [options]\n"); + printf("\t-a | --active sets bootnum active\n"); + printf("\t-A | --inactive sets bootnum inactive\n"); + printf("\t-b | --bootnum XXXX modify BootXXXX (hex)\n"); + printf("\t-B | --delete-bootnum delete bootnum (hex)\n"); + printf("\t-c | --create create new variable bootnum and add to bootorder\n"); + printf("\t-d | --disk disk (defaults to /dev/sda) containing loader\n"); + printf("\t-e | --edd [1|3|-1] force EDD 1.0 or 3.0 creation variables, or guess\n"); + printf("\t-E | --device num EDD 1.0 device number (defaults to 0x80)\n"); + printf("\t-g | --gpt force disk with invalid PMBR to be treated as GPT\n"); + printf("\t-H | --acpi_hid XXXX set the ACPI HID (used with -i)\n"); + printf("\t-i | --iface name create a netboot entry for the named interface\n"); + printf("\t-l | --loader name (defaults to \\elilo.efi)\n"); + printf("\t-L | --label label Boot manager display label (defaults to \"Linux\")\n"); + printf("\t-n | --bootnext XXXX set BootNext to XXXX (hex)\n"); + printf("\t-N | --delete-bootnext delete BootNext\n"); + printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,... explicitly set BootOrder (hex)\n"); + printf("\t-O | --delete-bootorder delete BootOrder\n"); + printf("\t-p | --part part (defaults to 1) containing loader\n"); + printf("\t-q | --quiet be quiet\n"); + printf("\t-t | --test filename don't write to NVRAM, write to filename.\n"); + printf("\t-u | --unicode | --UCS-2 pass extra args as UCS-2 (default is ASCII)\n"); + printf("\t-U | --acpi_uid XXXX set the ACPI UID (used with -i)\n"); + printf("\t-v | --verbose print additional information\n"); + printf("\t-V | --version return version and exit\n"); + printf("\t-w | --write-signature write unique sig to MBR if needed\n"); +} + +static void +set_default_opts() +{ + memset(&opts, 0, sizeof(opts)); + opts.bootnum = -1; /* auto-detect */ + opts.bootnext = -1; /* Don't set it */ + opts.active = -1; /* Don't set it */ + opts.edd10_devicenum = 0x80; + opts.loader = "\\elilo.efi"; + opts.label = "Linux"; + opts.disk = "/dev/sda"; + opts.iface = NULL; + opts.part = 1; + opts.acpi_hid = -1; + opts.acpi_uid = -1; +} + +static void +parse_opts(int argc, char **argv) +{ + int c, num, rc; + int option_index = 0; + + while (1) + { + static struct option long_options[] = + /* name, has_arg, flag, val */ + { + {"active", no_argument, 0, 'a'}, + {"inactive", no_argument, 0, 'A'}, + {"bootnum", required_argument, 0, 'b'}, + {"delete-bootnum", no_argument, 0, 'B'}, + {"create", no_argument, 0, 'c'}, + {"disk", required_argument, 0, 'd'}, + {"iface", required_argument, 0, 'i'}, + {"acpi_hid", required_argument, 0, 'H' }, + {"edd-device", required_argument, 0, 'E'}, + {"edd30", required_argument, 0, 'e'}, + {"gpt", no_argument, 0, 'g'}, + {"loader", required_argument, 0, 'l'}, + {"label", required_argument, 0, 'L'}, + {"bootnext", required_argument, 0, 'n'}, + {"delete-bootnext", no_argument, 0, 'N'}, + {"bootorder", required_argument, 0, 'o'}, + {"delete-bootorder", no_argument, 0, 'O'}, + {"part", required_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"test", required_argument, 0, 't'}, + {"unicode", no_argument, 0, 'u'}, + {"UCS-2", no_argument, 0, 'u'}, + {"acpi_uid", required_argument, 0, 'U' }, + {"verbose", optional_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"write-signature", no_argument, 0, 'w'}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, + "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:uU:v::Vw", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 'a': + opts.active = 1; + break; + case 'A': + opts.active = 0; + break; + case 'B': + opts.delete_boot = 1; + break; + case 'b': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.bootnum = num; + break; + case 'c': + opts.create = 1; + break; + case 'd': + opts.disk = optarg; + break; + case 'e': + rc = sscanf(optarg, "%d", &num); + if (rc == 1) opts.edd_version = num; + break; + case 'E': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.edd10_devicenum = num; + break; + case 'g': + opts.forcegpt = 1; + break; + case 'H': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.acpi_hid = num; + break; + case 'i': + opts.iface = optarg; + break; + case 'l': + opts.loader = optarg; + break; + case 'L': + opts.label = optarg; + break; + case 'N': + opts.delete_bootnext = 1; + break; + case 'n': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.bootnext = num; + break; + case 'o': + opts.bootorder = optarg; + break; + case 'O': + opts.delete_bootorder = 1; + break; + case 'p': + rc = sscanf(optarg, "%u", &num); + if (rc == 1) opts.part = num; + break; + case 'q': + opts.quiet = 1; + break; + case 't': + opts.testfile = optarg; + break; + case 'u': + opts.unicode = 1; + break; + + case 'U': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.acpi_uid = num; + break; + case 'v': + opts.verbose = 1; + if (optarg) { + if (!strcmp(optarg, "v")) opts.verbose = 2; + if (!strcmp(optarg, "vv")) opts.verbose = 3; + rc = sscanf(optarg, "%d", &num); + if (rc == 1) opts.verbose = num; + } + break; + case 'V': + opts.showversion = 1; + break; + + case 'w': + opts.write_signature = 1; + break; + + default: + usage(); + exit(1); + } + } + + if (optind < argc) { + opts.argc = argc; + opts.argv = argv; + opts.optind = optind; + } +} + + +int +main(int argc, char **argv) +{ + struct dirent **boot_names = NULL; + var_entry_t *new_boot = NULL; + int num, num_boot_names=0; + + set_default_opts(); + parse_opts(argc, argv); + if (opts.showversion) { + printf("version %s\n", EFIBOOTMGR_VERSION); + return 0; + } + + if (opts.iface && opts.acpi_hid == -1 && opts.acpi_uid == -1) { + fprintf(stderr, "\nYou must specify the ACPI HID and UID when using -i.\n\n"); + return 1; + } + + if (!opts.testfile) { + num_boot_names = read_boot_var_names(&boot_names); + read_vars(boot_names, num_boot_names, &boot_entry_list); + set_var_nums("Boot%04x-%*s", &boot_entry_list); + + if (opts.delete_boot) { + if (opts.bootnum == -1) + fprintf(stderr, "\nYou must specify a boot entry to delete (see the -b option).\n\n"); + else + delete_boot_var(opts.bootnum); + } + + if (opts.active >= 0) { + set_active_state(); + } + } + + if (opts.create) { + warn_duplicate_name(&boot_entry_list); + new_boot = make_boot_var(&boot_entry_list); + /* Put this boot var in the right BootOrder */ + if (!opts.testfile && new_boot) + add_to_boot_order(new_boot->num); + } + + if (!opts.testfile) { + + if (opts.delete_bootorder) { + delete_boot_order(); + } + + if (opts.bootorder) { + set_boot_order(); + } + + + if (opts.delete_bootnext) { + delete_boot_next(); + } + + if (opts.bootnext >= 0) { + set_boot_next(opts.bootnext & 0xFFFF); + } + + if (!opts.quiet) { + num = read_boot_next(); + if (num != -1 ) { + printf("BootNext: %04x\n", num); + } + num = read_boot_current(); + if (num != -1) { + printf("BootCurrent: %04x\n", num); + } + show_boot_order(); + show_boot_vars(); + } + } + free_dirents(boot_names, num_boot_names); + return 0; +} + diff --git a/src/efibootmgr/module.mk b/src/efibootmgr/module.mk new file mode 100644 index 0000000..7764641 --- /dev/null +++ b/src/efibootmgr/module.mk @@ -0,0 +1,22 @@ +efibootmgr_SRCDIR := src/efibootmgr +efibootmgr_OBJECTS := efibootmgr.o +efibootmgr_TARGETS := efibootmgr +efibootmgr_FULLTARGET := \ + $(patsubst %, $(efibootmgr_SRCDIR)/%, $(efibootmgr_TARGETS)) +efibootmgr_FULLOBJECT := \ + $(patsubst %, $(efibootmgr_SRCDIR)/%, $(efibootmgr_OBJECT)) + +efibootmgr_LIBS := crc32.o disk.o efi.o efichar.o gpt.o scsi_ioctls.o \ + unparse_path.o +efibootmgr_LIBDIR := src/lib +efibootmgr_FULLLIB := \ + $(patsubst %,$(efibootmgr_LIBDIR)/%,$(efibootmgr_LIBS)) + +ALLDEPS += $(efibootmgr_FULLTARGET) +CLEANLIST += $(efibootmgr_FULLTARGET) +CLEANLIST += $(efibootmgr_FULLOBJECT) +bindir_TARGETS += $(efibootmgr_FULLTARGET) + +$(efibootmgr_FULLTARGET): \ + $(efibootmgr_FULLOBJECT) \ + $(efibootmgr_FULLLIB) diff --git a/src/include/crc32.h b/src/include/crc32.h new file mode 100644 index 0000000..f35f9e4 --- /dev/null +++ b/src/include/crc32.h @@ -0,0 +1,34 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1998-2000 Free Software Foundation, Inc. + + crc32.h + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _CRC32_H +#define _CRC32_H + +#include + +/* + * This computes a 32 bit CRC of the data in the buffer, and returns the CRC. + * The polynomial used is 0xedb88320. + */ + +extern uint32_t crc32 (const void *buf, unsigned long len, uint32_t seed); + +#endif /* _CRC32_H */ diff --git a/src/include/disk.h b/src/include/disk.h new file mode 100644 index 0000000..eb93d10 --- /dev/null +++ b/src/include/disk.h @@ -0,0 +1,90 @@ +/* + disk.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DISK_H +#define _DISK_H + +#include + +/* Snagged from linux/include/asm-ia64/ioctl.h */ +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to create numbers */ +#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) + + +/* Snagged from linux/include/linux/fs.h */ +#define BLKGETSIZE _IO(0x12,96) /* return device size */ + + +enum _bus_type {bus_type_unknown, isa, pci}; +enum _interface_type {interface_type_unknown, + ata, atapi, scsi, usb, + i1394, fibre, i2o, md}; + +int disk_get_pci(int fd, + unsigned char *bus, + unsigned char *device, + unsigned char *function); + +int disk_info_from_fd(int fd, + int *interface_type, + unsigned int *controllernum, + unsigned int *disknum, + unsigned char *part); + +int disk_get_partition_info (int fd, + uint32_t num, + uint64_t *start, uint64_t *size, + char *signature, + uint8_t *mbr_type, uint8_t *signature_type); + + +int disk_get_size(int fd, long *size); +int get_sector_size(int fd); + + +#endif diff --git a/src/include/efi.h b/src/include/efi.h new file mode 100644 index 0000000..b53187e --- /dev/null +++ b/src/include/efi.h @@ -0,0 +1,359 @@ +/* + efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EFI_H +#define EFI_H + +/* + * Extensible Firmware Interface + * Based on 'Extensible Firmware Interface Specification' + * version 1.02, 12 December, 2000 + */ +#include + +#define BITS_PER_LONG (sizeof(unsigned long) * 8) + +#define EFI_ERROR(x) ((x) | (1L << (BITS_PER_LONG - 1))) + +#define EFI_SUCCESS 0 +#define EFI_LOAD_ERROR EFI_ERROR(1) +#define EFI_INVALID_PARAMETER EFI_ERROR(2) +#define EFI_UNSUPPORTED EFI_ERROR(3) +#define EFI_BAD_BUFFER_SIZE EFI_ERROR(4) +#define EFI_BUFFER_TOO_SMALL EFI_ERROR(5) +#define EFI_NOT_FOUND EFI_ERROR(14) +#define EFI_OUT_OF_RESOURCES EFI_ERROR(15) + + +/******************************************************* + * Boot Option Attributes + *******************************************************/ +#define LOAD_OPTION_ACTIVE 0x00000001 + +/****************************************************** + * EFI Variable Attributes + ******************************************************/ +#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 + + +typedef struct { + uint8_t b[16]; +} efi_guid_t; + +#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ +((efi_guid_t) \ +{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) + + +/****************************************************** + * GUIDs + ******************************************************/ +#define DEVICE_PATH_PROTOCOL \ +EFI_GUID( 0x09576e91, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_GLOBAL_VARIABLE \ +EFI_GUID( 0x8BE4DF61, 0x93CA, 0x11d2, 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C) +#define EDD10_HARDWARE_VENDOR_PATH_GUID \ +EFI_GUID( 0xCF31FAC5, 0xC24E, 0x11d2, 0x85, 0xF3, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) +#define BLKX_UNKNOWN_GUID \ +EFI_GUID( 0x47c7b225, 0xc42a, 0x11d2, 0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define DIR_UNKNOWN_GUID \ +EFI_GUID( 0x47c7b227, 0xc42a, 0x11d2, 0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define ESP_UNKNOWN_GUID \ +EFI_GUID( 0x47c7b226, 0xc42a, 0x11d2, 0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + +static inline int +efi_guidcmp(efi_guid_t left, efi_guid_t right) +{ + return memcmp(&left, &right, sizeof (efi_guid_t)); +} + + +typedef unsigned long efi_status_t; +typedef uint8_t efi_bool_t; +typedef uint16_t efi_char16_t; /* UNICODE character */ + +typedef struct _efi_variable_t { + efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; + efi_guid_t VendorGuid; + uint64_t DataSize; + uint8_t Data[1024]; + efi_status_t Status; + uint32_t Attributes; +} __attribute__((packed)) efi_variable_t; + + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t data[1]; +} __attribute__((packed)) EFI_DEVICE_PATH; + +typedef struct { + uint32_t attributes; + uint16_t file_path_list_length; + efi_char16_t description[1]; + EFI_DEVICE_PATH _unused_file_path_list[1]; +} __attribute__((packed)) EFI_LOAD_OPTION; + + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t _HID; + uint32_t _UID; +} __attribute__((packed)) ACPI_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + efi_guid_t vendor_guid; + uint8_t data[1]; +} __attribute__((packed)) VENDOR_DEVICE_PATH; + +#define EDD10_HARDWARE_VENDOR_PATH_LENGTH 24 + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t function; + uint8_t device; +} __attribute__((packed)) PCI_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t socket; +} __attribute__((packed)) PCCARD_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t memory_type; + uint64_t start; + uint64_t end; +} __attribute__((packed)) MEMORY_MAPPED_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t controller; +} __attribute__((packed)) CONTROLLER_DEVICE_PATH; + + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint16_t id; + uint16_t lun; +} __attribute__((packed)) SCSI_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t primary_secondary; + uint8_t slave_master; + uint16_t lun; +} __attribute__((packed)) ATAPI_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t reserved; + uint64_t wwn; + uint64_t lun; +} __attribute__((packed)) FIBRE_CHANNEL_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t reserved; + uint64_t guid; +} __attribute__((packed)) I1394_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t port; + uint8_t endpoint; +} __attribute__((packed)) USB_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint16_t vendor; + uint16_t product; + uint8_t class; + uint8_t subclass; + uint8_t protocol; +} __attribute__((packed)) USB_CLASS_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t tid; +} __attribute__((packed)) I2O_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t macaddr[32]; + uint8_t iftype; +} __attribute__((packed)) MAC_ADDR_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t local_ip; + uint32_t remote_ip; + uint16_t local_port; + uint16_t remote_port; + uint16_t protocol; + uint8_t static_addr; +} __attribute__((packed)) IPv4_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint8_t local_ip[16]; + uint8_t remote_ip[16]; + uint16_t local_port; + uint16_t remote_port; + uint16_t protocol; + uint8_t static_addr; +} __attribute__((packed)) IPv6_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t reserved; + uint64_t node_guid; + uint64_t ioc_guid; + uint64_t id; +} __attribute__((packed)) INFINIBAND_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t reserved; + uint64_t baud_rate; + uint8_t data_bits; + uint8_t parity; + uint8_t stop_bits; +} __attribute__((packed)) UART_DEVICE_PATH; + + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t part_num; + uint64_t start; + uint64_t size; + uint8_t signature[16]; + uint8_t mbr_type; + uint8_t signature_type; + uint8_t padding[6]; /* Emperically needed */ +} __attribute__((packed)) HARDDRIVE_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint32_t boot_entry; + uint64_t start; + uint64_t size; +} __attribute__((packed)) CDROM_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + efi_char16_t path_name[1]; +} __attribute__((packed)) FILE_PATH_DEVICE_PATH; + + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + efi_guid_t guid; +} __attribute__((packed)) MEDIA_PROTOCOL_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; + uint16_t device_type; + uint16_t status_flag; + uint8_t description[1]; +} __attribute__((packed)) BIOS_BOOT_SPEC_DEVICE_PATH; + +typedef struct { + uint8_t type; + uint8_t subtype; + uint16_t length; +} __attribute__((packed)) END_DEVICE_PATH; + + +/* Used for ACPI _HID */ +#define EISAID_PNP0A03 0xa0341d0 + +#define PROC_DIR_EFI_VARS "/proc/efi/vars/" + + + +/* Exported functions */ + +efi_status_t read_variable(char *name, efi_variable_t *var); +efi_status_t write_variable(efi_variable_t *var); +int make_linux_efi_variable(efi_variable_t *var, + unsigned int free_number); +char * efi_guid_unparse(efi_guid_t *guid, char *out); +EFI_DEVICE_PATH *load_option_path(EFI_LOAD_OPTION *option); + + + + + +#endif /* _ASM_IA64_EFI_H */ diff --git a/src/include/efibootmgr.h b/src/include/efibootmgr.h new file mode 100644 index 0000000..163793f --- /dev/null +++ b/src/include/efibootmgr.h @@ -0,0 +1,57 @@ +/* + efibootmgr.h - Manipulates EFI variables as exported in /proc/efi/vars + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _EFIBOOTMGR_H +#define _EFIBOOTMGR_H + +typedef struct { + int argc; + char **argv; + int optind; + char *disk; + char *iface; + char *loader; + char *label; + char *bootorder; + char *testfile; + uint32_t part; + int edd_version; + int edd10_devicenum; + int bootnum; + int bootnext; + int verbose; + int active; + uint32_t acpi_hid; + uint32_t acpi_uid; + unsigned int delete_boot:1; + unsigned int delete_bootorder:1; + unsigned int delete_bootnext:1; + unsigned int quiet:1; + unsigned int showversion:1; + unsigned int create:1; + unsigned int unicode:1; + unsigned int write_signature:1; + unsigned int forcegpt:1; +} efibootmgr_opt_t; + + +extern efibootmgr_opt_t opts; + +#endif diff --git a/src/include/efichar.h b/src/include/efichar.h new file mode 100644 index 0000000..9962707 --- /dev/null +++ b/src/include/efichar.h @@ -0,0 +1,32 @@ +/* + efichar.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _EFICHAR_H +#define _EFICHAR_H + +int efichar_strlen(const efi_char16_t *p, int max); +int efichar_strsize(const efi_char16_t *p); +unsigned long efichar_from_char(efi_char16_t *dest, const char *src, size_t dest_len); +unsigned long efichar_to_char(char *dest, const efi_char16_t *src, size_t dest_len); +int efichar_strcmp(const efi_char16_t *s1, const efi_char16_t *s2); +int efichar_char_strcmp(const char *s1, const efi_char16_t *s2); +int efichar_strncpy(efi_char16_t *s1, const efi_char16_t *s2, int max); + +#endif diff --git a/src/include/gpt.h b/src/include/gpt.h new file mode 100644 index 0000000..8944885 --- /dev/null +++ b/src/include/gpt.h @@ -0,0 +1,177 @@ +/* + gpt.[ch] + + Copyright (C) 2000-2001 Dell Computer Corporation + + EFI GUID Partition Table handling + Per Intel EFI Specification v1.02 + http://developer.intel.com/technology/efi/efi.htm + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _GPT_H +#define _GPT_H + + +#include +#include "efi.h" + +#define EFI_PMBR_OSTYPE_EFI 0xEF +#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE +#define MSDOS_MBR_SIGNATURE 0xaa55 +#define GPT_BLOCK_SIZE 512 + + +#define GPT_HEADER_SIGNATURE 0x5452415020494645 +#define GPT_HEADER_REVISION_V1_02 0x00010200 +#define GPT_HEADER_REVISION_V1_00 0x00010000 +#define GPT_HEADER_REVISION_V0_99 0x00009900 +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +#define PARTITION_SYSTEM_GUID \ + EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ + 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) +#define LEGACY_MBR_PARTITION_GUID \ + EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ + 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) +#define PARTITION_MSFT_RESERVED_GUID \ + EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ + 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) +#define PARTITION_BASIC_DATA_GUID \ + EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ + 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) +#define PARTITION_LINUX_RAID_GUID \ + EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ + 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) +#define PARTITION_LINUX_SWAP_GUID \ + EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ + 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) +#define PARTITION_LINUX_LVM_GUID \ + EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ + 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) + +typedef struct _gpt_header { + uint64_t signature; + uint32_t revision; + uint32_t header_size; + uint32_t header_crc32; + uint32_t reserved1; + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + efi_guid_t disk_guid; + uint64_t partition_entry_lba; + uint32_t num_partition_entries; + uint32_t sizeof_partition_entry; + uint32_t partition_entry_array_crc32; + uint8_t reserved2[GPT_BLOCK_SIZE - 92]; +} __attribute__ ((packed)) gpt_header; + +typedef struct _gpt_entry_attributes { + uint64_t required_to_function:1; + uint64_t reserved:47; + uint64_t type_guid_specific:16; +} __attribute__ ((packed)) gpt_entry_attributes; + +typedef struct _gpt_entry { + efi_guid_t partition_type_guid; + efi_guid_t unique_partition_guid; + uint64_t starting_lba; + uint64_t ending_lba; + gpt_entry_attributes attributes; + efi_char16_t partition_name[72 / sizeof(efi_char16_t)]; +} __attribute__ ((packed)) gpt_entry; + + +/* + These values are only defaults. The actual on-disk structures + may define different sizes, so use those unless creating a new GPT disk! +*/ + +#define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384 +/* + Number of actual partition entries should be calculated + as: +*/ +#define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \ + (GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \ + sizeof(gpt_entry)) + + +typedef struct _partition_record { + uint8_t boot_indicator; /* Not used by EFI firmware. Set to 0x80 to indicate that this + is the bootable legacy partition. */ + uint8_t start_head; /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t start_sector; /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t start_track; /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t os_type; /* OS type. A value of 0xEF defines an EFI system partition. + Other values are reserved for legacy operating systems, and + allocated independently of the EFI specification. */ + uint8_t end_head; /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t end_sector; /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t end_track; /* End of partition in CHS address, not used by EFI firmware. */ + uint32_t starting_lba; /* Starting LBA address of the partition on the disk. Used by + EFI firmware to define the start of the partition. */ + uint32_t size_in_lba; /* Size of partition in LBA. Used by EFI firmware to determine + the size of the partition. */ +} __attribute__ ((packed)) partition_record; + + +/* Protected Master Boot Record & Legacy MBR share same structure */ +/* Needs to be packed because the u16s force misalignment. */ + +typedef struct _legacy_mbr { + uint8_t bootcode[440]; + uint32_t unique_mbr_signature; + uint16_t unknown; + partition_record partition[4]; + uint16_t signature; +} __attribute__ ((packed)) legacy_mbr; + + + + +#define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +/* Functions */ +int gpt_disk_get_partition_info (int fd, + uint32_t num, + uint64_t *start, uint64_t *size, + char *signature, + uint8_t *mbr_type, uint8_t *signature_type); + + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/src/include/list.h b/src/include/list.h new file mode 100644 index 0000000..a306043 --- /dev/null +++ b/src/include/list.h @@ -0,0 +1,169 @@ +/* + Copied from the Linux 2.4.4 kernel, in linux/include/linux/list.h +*/ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + + +/* + * 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; +}; + +typedef struct list_head list_t; + +#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); +} + +/** + * 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_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; +} + +/** + * 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) +{ + struct list_head *first = list->next; + + if (first != list) { + 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_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) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->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_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) + + + +#endif diff --git a/src/include/module.mk b/src/include/module.mk new file mode 100644 index 0000000..071d6df --- /dev/null +++ b/src/include/module.mk @@ -0,0 +1 @@ +CFLAGS += -Isrc/include diff --git a/src/include/scsi_ioctls.h b/src/include/scsi_ioctls.h new file mode 100644 index 0000000..558824f --- /dev/null +++ b/src/include/scsi_ioctls.h @@ -0,0 +1,45 @@ +/* + scsi_ioctls.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SCSI_IOCTLS_H +#define _SCSI_IOCTLS_H + +#include + + +/* Snagged from linux/include/scsi/scsi.h */ +#define SCSI_IOCTL_GET_IDLUN 0x5382 +#define SCSI_IOCTL_GET_PCI 0x5387 + +typedef struct scsi_idlun { + uint32_t dev_id; + uint32_t host_unique_id; +} Scsi_Idlun; + + +inline int get_scsi_idlun(int fd, Scsi_Idlun *idlun); +inline int get_scsi_pci(int fd, char *slot_name); +int idlun_to_components (Scsi_Idlun *idlun, + unsigned char *host, + unsigned char *channel, + unsigned char *id, + unsigned char *lun); + +#endif diff --git a/src/include/unparse_path.h b/src/include/unparse_path.h new file mode 100644 index 0000000..0324bfe --- /dev/null +++ b/src/include/unparse_path.h @@ -0,0 +1,35 @@ +/* + unparse_path.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _UNPARSE_PATH_H +#define _UNPARSE_PATH_H + +#include +#include "efi.h" + +#define OFFSET_OF(struct_type, member) \ + ((unsigned long) ((char *) &((struct_type*) 0)->member)) + +uint64_t unparse_path(char *buffer, EFI_DEVICE_PATH *path, uint16_t pathsize); +void dump_raw_data(void *data, uint64_t length); +unsigned long unparse_raw(char *buffer, uint8_t *p, uint64_t length); +unsigned long unparse_raw_text(char *buffer, uint8_t *p, uint64_t length); + +#endif diff --git a/src/lib/Makefile b/src/lib/Makefile new file mode 100644 index 0000000..6615d46 --- /dev/null +++ b/src/lib/Makefile @@ -0,0 +1,2 @@ +%: + +make -C ../ $* diff --git a/src/lib/crc32.c b/src/lib/crc32.c new file mode 100644 index 0000000..e9086b1 --- /dev/null +++ b/src/lib/crc32.c @@ -0,0 +1,124 @@ +/* + * Dec 5, 2000 Matt Domsch + * - Copied crc32.c from the linux/drivers/net/cipe directory. + * - Now pass seed as an arg + * - changed len to be an unsigned long + * - changed crc32val to be a register + * - License remains unchanged! It's still GPL-compatable! + */ + + /* ============================================================= */ + /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ + /* code or tables extracted from it, as desired without restriction. */ + /* */ + /* First, the polynomial itself and its table of feedback terms. The */ + /* polynomial is */ + /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ + /* */ + /* Note that we take it "backwards" and put the highest-order term in */ + /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ + /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ + /* the MSB being 1. */ + /* */ + /* Note that the usual hardware shift register implementation, which */ + /* is what we're using (we're merely optimizing it by doing eight-bit */ + /* chunks at a time) shifts bits into the lowest-order term. In our */ + /* implementation, that means shifting towards the right. Why do we */ + /* do it this way? Because the calculated CRC must be transmitted in */ + /* order from highest-order term to lowest-order term. UARTs transmit */ + /* characters in order from LSB to MSB. By storing the CRC this way, */ + /* we hand it to the UART in the order low-byte to high-byte; the UART */ + /* sends each low-bit to hight-bit; and the result is transmission bit */ + /* by bit from highest- to lowest-order term without requiring any bit */ + /* shuffling on our part. Reception works similarly. */ + /* */ + /* The feedback terms table consists of 256, 32-bit entries. Notes: */ + /* */ + /* The table can be generated at runtime if desired; code to do so */ + /* is shown later. It might not be obvious, but the feedback */ + /* terms simply represent the results of eight shift/xor opera- */ + /* tions for all combinations of data and CRC register values. */ + /* */ + /* The values must be right-shifted by eight bits by the "updcrc" */ + /* logic; the shift must be unsigned (bring in zeroes). On some */ + /* hardware you could probably optimize the shift in assembler by */ + /* using byte-swap instructions. */ + /* polynomial $edb88320 */ + /* */ + /* -------------------------------------------------------------------- */ + +#include + +static uint32_t crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +uint32_t +crc32(const void *buf, unsigned long len, uint32_t seed) +{ + unsigned long i; + register uint32_t crc32val; + const unsigned char *s = buf; + + crc32val = seed; + for (i = 0; i < len; i ++) + { + crc32val = + crc32_tab[(crc32val ^ s[i]) & 0xff] ^ + (crc32val >> 8); + } + return crc32val; +} diff --git a/src/lib/disk.c b/src/lib/disk.c new file mode 100644 index 0000000..6581040 --- /dev/null +++ b/src/lib/disk.c @@ -0,0 +1,502 @@ +/* + disk.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "disk.h" +#include "scsi_ioctls.h" +#include "gpt.h" +#include "efibootmgr.h" + +#define BLKSSZGET _IO(0x12,104) /* get block device sector size */ + +int +disk_info_from_fd(int fd, + int *interface_type, + unsigned int *controllernum, + unsigned int *disknum, + unsigned char *part) +{ + struct stat buf; + int rc; + uint64_t major; + unsigned char minor; + memset(&buf, 0, sizeof(struct stat)); + rc = fstat(fd, &buf); + if (rc == -1) { + perror("stat"); + return 1; + } + if (!(S_ISBLK(buf.st_mode) || S_ISREG(buf.st_mode))) { + printf("Cannot stat non-block or non-regular file\n"); + return 1; + } + major = buf.st_dev >> 8; + minor = buf.st_dev && 0xFF; + + /* IDE disks can have up to 64 partitions, or 6 bits worth, + * and have one bit for the disk number. + * This leaves an extra bit at the top. + */ + if (major == 3) { + *disknum = (minor >> 6) & 1; + *controllernum = (major - 3 + 0) + *disknum; + *interface_type = ata; + *part = minor & 0x3F; + return 0; + } + else if (major == 22) { + *disknum = (minor >> 6) & 1; + *controllernum = (major - 22 + 2) + *disknum; + *interface_type = ata; + *part = minor & 0x3F; + return 0; + } + else if (major >= 33 && major <= 34) { + *disknum = (minor >> 6) & 1; + *controllernum = (major - 33 + 4) + *disknum; + *interface_type = ata; + *part = minor & 0x3F; + return 0; + } + else if (major >= 56 && major <= 57) { + *disknum = (minor >> 6) & 1; + *controllernum = (major - 56 + 8) + *disknum; + *interface_type = ata; + *part = minor & 0x3F; + return 0; + } + else if (major >= 88 && major <= 91) { + *disknum = (minor >> 6) & 1; + *controllernum = (major - 88 + 12) + *disknum; + *interface_type = ata; + *part = minor & 0x3F; + return 0; + } + + /* I2O disks can have up to 16 partitions, or 4 bits worth. */ + if (major >= 80 && major <= 87) { + *interface_type = i2o; + *disknum = 16*(major-80) + (minor >> 4); + *part = (minor & 0xF); + return 0; + } + + /* SCSI disks can have up to 16 partitions, or 4 bits worth + * and have one bit for the disk number. + */ + if (major == 8) { + *interface_type = scsi; + *disknum = (minor >> 4); + *part = (minor & 0xF); + return 0; + } + else if ( major >= 65 && major <= 71) { + *interface_type = scsi; + *disknum = 16*(major-64) + (minor >> 4); + *part = (minor & 0xF); + return 0; + } + + printf("Unknown interface type.\n"); + return 1; +} + +static int +disk_get_scsi_pci(int fd, + unsigned char *bus, + unsigned char *device, + unsigned char *function) +{ + int rc, usefd=fd; + struct stat buf; + char slot_name[8]; + unsigned int b=0,d=0,f=0; + memset(&buf, 0, sizeof(buf)); + rc = fstat(fd, &buf); + if (rc == -1) { + perror("stat"); + return 1; + } + if (S_ISREG(buf.st_mode)) { + /* can't call ioctl() on this file and have it succeed. + * instead, need to open the block device + * from /dev/. + */ + fprintf(stderr, "You must call this program with " + "a file name such as /dev/sda.\n"); + return 1; + } + + rc = get_scsi_pci(usefd, slot_name); + if (rc) { + perror("get_scsi_pci"); + return rc; + } + rc = sscanf(slot_name, "%x:%x.%x", &b,&d,&f); + if (rc != 3) { + printf("sscanf failed\n"); + return 1; + } + *bus = b & 0xFF; + *device = d & 0xFF; + *function = f & 0xFF; + return 0; +} + +/* + * The PCI interface treats multi-function devices as independent + * devices. The slot/function address of each device is encoded + * in a single byte as follows: + * + * 7:3 = slot + * 2:0 = function + * + * pci bus 00 device 39 vid 8086 did 7111 channel 1 + * 00:07.1 + */ +#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) +#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_FUNC(devfn) ((devfn) & 0x07) + +static int +disk_get_ide_pci(int fd, + unsigned char *bus, + unsigned char *device, + unsigned char *function) +{ + int num_scanned, procfd; + unsigned int b=0,d=0,disknum=0, controllernum=0; + unsigned char part=0; + char procname[80], infoline[80]; + size_t read_count; + int interface_type; + int rc; + + rc = disk_info_from_fd(fd, &interface_type, &controllernum, + &disknum, &part); + if (rc) return rc; + + + sprintf(procname, "/proc/ide/ide%d/config", controllernum); + + procfd = open(procname, O_RDONLY); + if (!procfd) { + perror("opening /proc/ide/ide*/config"); + return 1; + } + read_count = read(procfd, infoline, sizeof(infoline)-1); + close(procfd); + + num_scanned = sscanf(infoline, + "pci bus %x device %x vid %*x did %*x channel %*x", + &b, &d); + + if (num_scanned == 2) { + *bus = b; + *device = PCI_SLOT(d); + *function = PCI_FUNC(d); + } + return 0; +} + + + +#if 0 +/* this is a list of devices */ +static int +disk_get_md_parts(int fd) +{ + return 0; +} +#endif + +int +disk_get_pci(int fd, + unsigned char *bus, + unsigned char *device, + unsigned char *function) +{ + int interface_type=interface_type_unknown; + unsigned int controllernum=0, disknum=0; + unsigned char part=0; + + disk_info_from_fd(fd, + &interface_type, + &controllernum, + &disknum, + &part); + switch (interface_type) { + case ata: + return disk_get_ide_pci(fd, bus, device, function); + break; + case scsi: + return disk_get_scsi_pci(fd, bus, device, function); + break; + case i2o: + break; + case md: + break; + default: + break; + } + return 1; +} + +int +disk_get_size(int fd, long *size) +{ + return ioctl(fd, BLKGETSIZE, size); +} + +/** + * is_mbr_valid(): test MBR for validity + * @mbr: pointer to a legacy mbr structure + * + * Description: Returns 1 if MBR is valid, 0 otherwise. + * Validity depends on one thing: + * 1) MSDOS signature is in the last two bytes of the MBR + */ +static int +is_mbr_valid(legacy_mbr *mbr) +{ + if (!mbr) + return 0; + return (mbr->signature == MSDOS_MBR_SIGNATURE); +} + +/************************************************************ + * msdos_disk_get_extended partition_info() + * Requires: + * - open file descriptor fd + * - start, size + * Modifies: all these + * Returns: + * 0 on success + * non-zero on failure + * + ************************************************************/ + +static int +msdos_disk_get_extended_partition_info (int fd, legacy_mbr *mbr, + uint32_t num, + uint64_t *start, uint64_t *size) +{ + /* Until I can handle these... */ + fprintf(stderr, "Extended partition info not supported.\n"); + return 1; +} + +/************************************************************ + * msdos_disk_get_partition_info() + * Requires: + * - mbr + * - open file descriptor fd (for extended partitions) + * - start, size, signature, mbr_type, signature_type + * Modifies: all these + * Returns: + * 0 on success + * non-zero on failure + * + ************************************************************/ + +static int +msdos_disk_get_partition_info (int fd, legacy_mbr *mbr, + uint32_t num, + uint64_t *start, uint64_t *size, + char *signature, + uint8_t *mbr_type, uint8_t *signature_type) +{ + int rc; + long disk_size=0; + struct stat stat; + struct timeval tv; + + if (!mbr) return 1; + if (!is_mbr_valid(mbr)) return 1; + + *mbr_type = 0x01; + *signature_type = 0x01; + + if (!mbr->unique_mbr_signature && !opts.write_signature) { + + printf("\n\n******************************************************\n"); + printf("Warning! This MBR disk does not have a unique signature.\n"); + printf("If this is not the first disk found by EFI, you may not be able\n"); + printf("to boot from it without a unique signature.\n"); + printf("Run efibootmgr with the -w flag to write a unique signature\n"); + printf("to the disk.\n"); + printf("******************************************************\n\n"); + + } + else if (opts.write_signature) { + + /* MBR Signatures must be unique for the + EFI Boot Manager + to find the right disk to boot from */ + + rc = fstat(fd, &stat); + if (rc == -1) { + perror("stat disk"); + } + + rc = gettimeofday(&tv, NULL); + if (rc == -1) { + perror("gettimeofday"); + } + + /* Write the device type to the signature. + This should be unique per disk per system */ + mbr->unique_mbr_signature = tv.tv_usec << 16; + mbr->unique_mbr_signature |= stat.st_rdev & 0xFFFF; + + /* Write it to the disk */ + lseek(fd, 0, SEEK_SET); + write(fd, mbr, sizeof(*mbr)); + + } + *(uint32_t *)signature = mbr->unique_mbr_signature; + + + if (num > 4) { + /* Extended partition */ + return msdos_disk_get_extended_partition_info(fd, mbr, num, + start, size); + } + else if (num == 0) { + /* Whole disk */ + *start = 0; + disk_get_size(fd, &disk_size); + *size = disk_size; + } + else if (num >= 1 && num <= 4) { + /* Primary partition */ + *start = mbr->partition[num-1].starting_lba; + *size = mbr->partition[num-1].size_in_lba; + + } + return 0; +} + +/************************************************************ + * get_sector_size + * Requires: + * - filedes is an open file descriptor, suitable for reading + * Modifies: nothing + * Returns: + * sector size, or 512. + ************************************************************/ +int +get_sector_size(int filedes) +{ + int rc, sector_size = 512; + + rc = ioctl(filedes, BLKSSZGET, §or_size); + if (rc) + sector_size = 512; + return sector_size; +} + +/** + * disk_get_partition_info() + * @fd - open file descriptor to disk + * @num - partition number (1 is first partition on the disk) + * @start - partition starting sector returned + * @size - partition size (in sectors) returned + * @signature - partition signature returned + * @mbr_type - partition type returned + * @signature_type - signature type returned + * + * Description: Finds partition table info for given partition on given disk. + * Both GPT and MSDOS partition tables are tested for. + * Returns 0 on success, non-zero on failure + */ +int +disk_get_partition_info (int fd, + uint32_t num, + uint64_t *start, uint64_t *size, + char *signature, + uint8_t *mbr_type, uint8_t *signature_type) +{ + legacy_mbr *mbr; + void *mbr_unaligned; + off_t offset; + int this_bytes_read = 0; + int gpt_invalid=0, mbr_invalid=0; + int rc=0; + int sector_size = get_sector_size(fd); + + if (sizeof(*mbr) != sector_size) + return 1; + mbr_unaligned = malloc(sizeof(*mbr)+sector_size-1); + mbr = (legacy_mbr *) + (((unsigned long)mbr_unaligned + sector_size - 1) & + ~(unsigned long)(sector_size-1)); + memset(mbr, 0, sizeof(*mbr)); + offset = lseek(fd, 0, SEEK_SET); + this_bytes_read = read(fd, mbr, sizeof(*mbr)); + if (this_bytes_read < sizeof(*mbr)) { + rc=1; + goto error_free_mbr; + } + gpt_invalid = gpt_disk_get_partition_info(fd, num, + start, size, + signature, + mbr_type, + signature_type); + if (gpt_invalid) { + mbr_invalid = msdos_disk_get_partition_info(fd, mbr, num, + start, size, + signature, + mbr_type, + signature_type); + if (mbr_invalid) { + rc=1; + goto error_free_mbr; + } + } + error_free_mbr: + free(mbr_unaligned); + return rc; +} + +#ifdef DISK_EXE +int +main (int argc, char *argv[]) +{ + int fd, rc; + unsigned char bus=0,device=0,func=0; + if (argc <= 1) return 1; + fd = open(argv[1], O_RDONLY|O_DIRECT); + rc = disk_get_pci(fd, &bus, &device, &func); + if (!rc) { + printf("PCI %02x:%02x.%02x\n", bus, device, func); + } + return rc; +} +#endif diff --git a/src/lib/efi.c b/src/lib/efi.c new file mode 100644 index 0000000..0ab0339 --- /dev/null +++ b/src/lib/efi.c @@ -0,0 +1,692 @@ +/* + efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars + + Copyright (C) 2001,2003 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */ +typedef __uint32_t u32; /* ditto */ +typedef __uint16_t u16; /* ditto */ +typedef __uint8_t u8; /* ditto */ + +#include +#include "efi.h" +#include "efichar.h" +#include "scsi_ioctls.h" +#include "disk.h" +#include "efibootmgr.h" + +EFI_DEVICE_PATH * +load_option_path(EFI_LOAD_OPTION *option) +{ + char *p = (char *) option; + return (EFI_DEVICE_PATH *) + (p + sizeof(uint32_t) /* Attributes */ + + sizeof(uint16_t) /* FilePathListLength*/ + + efichar_strsize(option->description)); /* Description */ +} + +char * +efi_guid_unparse(efi_guid_t *guid, char *out) +{ + sprintf(out, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->b[3], guid->b[2], guid->b[1], guid->b[0], + guid->b[5], guid->b[4], guid->b[7], guid->b[6], + guid->b[8], guid->b[9], guid->b[10], guid->b[11], + guid->b[12], guid->b[13], guid->b[14], guid->b[15]); + return out; +} + + +efi_status_t +read_variable(char *name, efi_variable_t *var) +{ + int newnamesize; + char *newname; + int fd; + size_t readsize; + if (!name || !var) return EFI_INVALID_PARAMETER; + + newnamesize = strlen(PROC_DIR_EFI_VARS) + strlen(name) + 1; + newname = malloc(newnamesize); + if (!newname) return EFI_OUT_OF_RESOURCES; + sprintf(newname, "%s%s", PROC_DIR_EFI_VARS,name); + fd = open(newname, O_RDONLY); + if (fd == -1) { + free(newname); + return EFI_NOT_FOUND; + } + readsize = read(fd, var, sizeof(*var)); + if (readsize != sizeof(*var)) { + free(newname); + close(fd); + return EFI_INVALID_PARAMETER; + } + close(fd); + free(newname); + return var->Status; +} + +static efi_status_t +write_variable_to_file(efi_variable_t *var) +{ + int fd, byteswritten; + if (!var || !opts.testfile) return EFI_INVALID_PARAMETER; + + printf("Test mode: Writing to %s\n", opts.testfile); + fd = creat(opts.testfile, S_IRWXU); + if (fd == -1) { + perror("Couldn't write to testfile"); + return EFI_INVALID_PARAMETER; + } + + byteswritten = write(fd, var, sizeof(*var)); + if (byteswritten == -1) { + perror("Writing to testfile"); + + } + close(fd); + return EFI_SUCCESS; +} +/** + * select_variable_names() + * @d - dirent to compare against + * + * This ignores "." and ".." entries, and selects all others. + */ + +static int +select_variable_names(const struct dirent *d) +{ + if (!strcmp(d->d_name, ".") || + !strcmp(d->d_name, "..")) + return 0; + return 1; +} + +/** + * find_write_victim() + * @var - variable to be written + * @file - name of file to open for writing @var is returned. + * + * This ignores "." and ".." entries, and selects all others. + */ +static char * +find_write_victim(efi_variable_t *var, char file[PATH_MAX]) +{ + struct dirent **namelist = NULL; + int i, n, found=0; + char testname[PATH_MAX], *p; + + memset(testname, 0, sizeof(testname)); + n = scandir(PROC_DIR_EFI_VARS, &namelist, + select_variable_names, alphasort); + if (n < 0) { + perror("scandir " PROC_DIR_EFI_VARS); + fprintf(stderr, "You must 'modprobe efivars' first.\n"); + return NULL; + } + + p = testname; + efichar_to_char(p, var->VariableName, PATH_MAX); + p += strlen(p); + p += sprintf(p, "-"); + efi_guid_unparse(&var->VendorGuid, p); + + for (i=0; id_name, sizeof(testname))) { + found++; + sprintf(file, "%s%s", PROC_DIR_EFI_VARS, + namelist[i]->d_name); + break; + } + } + + while (n--) { + if (namelist[n]) { + free(namelist[n]); + namelist[n] = NULL; + } + } + free(namelist); + + if (!found) return NULL; + return file; +} + + +efi_status_t +write_variable(efi_variable_t *var) +{ + int fd; + size_t writesize; + char buffer[PATH_MAX], name[PATH_MAX], *p = NULL; + + if (!var) return EFI_INVALID_PARAMETER; + if (opts.testfile) return write_variable_to_file(var); + memset(buffer, 0, sizeof(buffer)); + memset(name, 0, sizeof(name)); + + p = find_write_victim(var, name); + if (!p) return EFI_INVALID_PARAMETER; + + fd = open(name, O_WRONLY); + if (fd == -1) { + sprintf(buffer, "write_variable():open(%s)", name); + perror(buffer); + return EFI_INVALID_PARAMETER; + } + writesize = write(fd, var, sizeof(*var)); + if (writesize != sizeof(*var)) { +#if 0 + sprintf(buffer, "write_variable():write(%s)", name); + perror(buffer); + dump_raw_data(var, sizeof(*var)); +#endif + close(fd); + return EFI_INVALID_PARAMETER; + + } + close(fd); + return EFI_SUCCESS; +} + + +static int +get_edd_version() +{ + efi_status_t status; + efi_variable_t var; + efi_guid_t guid = BLKX_UNKNOWN_GUID; + char name[80], text_guid[40]; + ACPI_DEVICE_PATH *path = (ACPI_DEVICE_PATH *)&(var.Data); + int rc = 0; + + /* Allow global user option override */ + + switch (opts.edd_version) + { + case 0: /* No EDD information */ + return 0; + break; + case 1: /* EDD 1.0 */ + return 1; + break; + case 3: /* EDD 3.0 */ + return 3; + break; + default: + break; + } + + + memset(&var, 0, sizeof(efi_variable_t)); + efi_guid_unparse(&guid, text_guid); + sprintf(name, "blk0-%s", text_guid); + + status = read_variable(name, &var); + if (status != EFI_SUCCESS) { + return 0; + } + if (path->type == 2 && path->subtype == 1) rc = 3; + else rc = 1; + return rc; +} + +/* + EFI_DEVICE_PATH, 0x01 (Hardware), 0x04 (Vendor), length 0x0018 + This needs to know what EFI device has the boot device. +*/ +static uint16_t +make_edd10_device_path(void *dest, uint32_t hardware_device) +{ + VENDOR_DEVICE_PATH *hw; + char buffer[EDD10_HARDWARE_VENDOR_PATH_LENGTH]; + efi_guid_t guid = EDD10_HARDWARE_VENDOR_PATH_GUID; + uint32_t *data; + memset(buffer, 0, sizeof(buffer)); + hw = (VENDOR_DEVICE_PATH *)buffer; + data = (uint32_t *)hw->data; + hw->type = 0x01; /* Hardware Device Path */ + hw->subtype = 0x04; /* Vendor */ + hw->length = EDD10_HARDWARE_VENDOR_PATH_LENGTH; + memcpy(&(hw->vendor_guid), &guid, sizeof(guid)); + *data = hardware_device; + memcpy(dest, buffer, hw->length); + return hw->length; +} + +static uint16_t +make_end_device_path(void *dest) +{ + END_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 0x7F; /* End of Hardware Device Path */ + p.subtype = 0xFF; /* End Entire Device Path */ + p.length = sizeof(p); + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_acpi_device_path(void *dest, uint32_t _HID, uint32_t _UID) +{ + ACPI_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 2; + p.subtype = 1; + p.length = sizeof(p); + p._HID = _HID; + p._UID = _UID; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_mac_addr_device_path(void *dest, char *mac, uint8_t iftype) +{ + + int i; + MAC_ADDR_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 3; + p.subtype = 11; + p.length = sizeof(p); + for (i=0; i < 14; i++) { + p.macaddr[i] = mac[i]; + } + p.iftype = iftype; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_pci_device_path(void *dest, uint8_t device, uint8_t function) +{ + PCI_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 1; + p.subtype = 1; + p.length = sizeof(p); + p.device = device; + p.function = function; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_scsi_device_path(void *dest, uint16_t id, uint16_t lun) +{ + SCSI_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 3; + p.subtype = 2; + p.length = sizeof(p); + p.id = id; + p.lun = lun; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_harddrive_device_path(void *dest, uint32_t num, uint64_t start, uint64_t size, + uint8_t *signature, + uint8_t mbr_type, uint8_t signature_type) +{ + HARDDRIVE_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 4; + p.subtype = 1; + p.length = sizeof(p); + p.part_num = num; + p.start = start; + p.size = size; + if (signature) memcpy(p.signature, signature, 16); + p.mbr_type = mbr_type; + p.signature_type = signature_type; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_file_path_device_path(void *dest, efi_char16_t *name) +{ + FILE_PATH_DEVICE_PATH *p; + char buffer[1024]; + int namelen = efichar_strlen(name, -1); + int namesize = efichar_strsize(name); + + memset(buffer, 0, sizeof(buffer)); + p = (FILE_PATH_DEVICE_PATH *)buffer; + p->type = 4; + p->subtype = 4; + p->length = 4 + namesize; + efichar_strncpy(p->path_name, + name, namelen); + + memcpy(dest, buffer, p->length); + return p->length; + +} + + + +static long +make_edd30_device_path(int fd, void *buffer) +{ + int rc=0; + unsigned char bus=0, device=0, function=0; + Scsi_Idlun idlun; + unsigned char host=0, channel=0, id=0, lun=0; + char *p = buffer; + + + rc = disk_get_pci(fd, &bus, &device, &function); + if (rc) return 0; + + memset(&idlun, 0, sizeof(idlun)); + rc = get_scsi_idlun(fd, &idlun); + if (rc) return 0; + idlun_to_components(&idlun, &host, &channel, &id, &lun); + + p += make_acpi_device_path (p, EISAID_PNP0A03, bus); + p += make_pci_device_path (p, device, function); + p += make_scsi_device_path (p, id, lun); + return ((void *)p - buffer); +} + +/** + * make_disk_load_option() + * @disk disk + * + * Returns 0 on error, length of load option created on success. + */ +char *make_disk_load_option(char *p, char *disk) +{ + int disk_fd=0; + char buffer[80]; + char signature[16]; + int rc, edd_version=0; + uint8_t mbr_type=0, signature_type=0; + uint64_t start=0, size=0; + efi_char16_t os_loader_path[40]; + + memset(signature, 0, sizeof(signature)); + + disk_fd = open(opts.disk, O_RDWR); + if (disk_fd == -1) { + sprintf(buffer, "Could not open disk %s", opts.disk); + perror(buffer); + return 0; + } + + if (opts.edd_version) { + edd_version = get_edd_version(); + + if (edd_version == 3) { + p += make_edd30_device_path(disk_fd, p); + } + else if (edd_version == 1) { + p += make_edd10_device_path(p, opts.edd10_devicenum); + } + } + + rc = disk_get_partition_info (disk_fd, opts.part, + &start, &size, signature, + &mbr_type, &signature_type); + + close(disk_fd); + + if (rc) { + fprintf(stderr, "Error: no partition information on disk %s.\n" + " Cowardly refusing to create a boot option.\n", + opts.disk); + return 0; + } + + p += make_harddrive_device_path (p, opts.part, + start, size, + signature, + mbr_type, signature_type); + + efichar_from_char(os_loader_path, opts.loader, sizeof(os_loader_path)); + p += make_file_path_device_path (p, os_loader_path); + p += make_end_device_path (p); + + return(p); +} + +/** + * make_net_load_option() + * @data - load option returned + * + * Returns 0 on error, length of load option created on success. + */ +char *make_net_load_option(char *p, char *iface) +{ + /* copied pretty much verbatim from the ethtool source */ + int fd = 0, err; + int bus, slot, func; + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, iface); + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)&drvinfo; + /* Open control socket */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + } + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + perror("Cannot get driver information"); + } + + err = sscanf(drvinfo.bus_info, "%2x:%2x.%x", &bus, &slot, &func); + if (err == 0) { + perror("Couldn't parse device location string."); + } + + p += make_acpi_device_path(p, opts.acpi_hid, opts.acpi_uid); + p += make_pci_device_path(p, (uint8_t)slot, (uint8_t)func); + + err = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (err < 0) { + perror("Cannot get hardware address."); + } + + p += make_mac_addr_device_path(p, ifr.ifr_ifru.ifru_hwaddr.sa_data, 0); + p += make_end_device_path (p); + + return(p); +} + +/** + * make_linux_load_option() + * @data - load option returned + * + * Returns 0 on error, length of load option created on success. + */ +static unsigned long +make_linux_load_option(void *data) +{ + EFI_LOAD_OPTION *load_option = data; + char *p = data, *q; + efi_char16_t description[64]; + unsigned long datasize=0; + + /* Write Attributes */ + if (opts.active) load_option->attributes = LOAD_OPTION_ACTIVE; + else load_option->attributes = 0; + + p += sizeof(uint32_t); + /* skip writing file_path_list_length */ + p += sizeof(uint16_t); + /* Write description. This is the text that appears on the screen for the load option. */ + memset(description, 0, sizeof(description)); + efichar_from_char(description, opts.label, sizeof(description)); + efichar_strncpy(load_option->description, description, sizeof(description)); + p += efichar_strsize(load_option->description); + + q = p; + + if (opts.iface) { + p = (char *)make_net_load_option(p, opts.iface); + } + + else { + p = (char *)make_disk_load_option(p, opts.iface); + } + + load_option->file_path_list_length = p - q; + + datasize = (uint8_t *)p - (uint8_t *)data; + return datasize; +} + +/* + * append_extra_args() + * appends all arguments from argv[] not snarfed by getopt + * as one long string onto data, up to maxchars. allow for nulls + */ + +static unsigned long +append_extra_args_ascii(void *data, unsigned long maxchars) +{ + char *p = data; + int i, appended=0; + unsigned long usedchars=0; + if (!data) return 0; + + + for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) { + p = strncpy(p, opts.argv[i], maxchars-usedchars-1); + p += strlen(p); + appended=1; + + usedchars = p - (char *)data; + + /* Put a space between args */ + if (i < (opts.argc-1)) { + + p = strncpy(p, " ", maxchars-usedchars-1); + p += strlen(p); + usedchars = p - (char *)data; + } + + } + /* Remember the NULL */ + if (appended) return strlen(data) + 1; + return 0; +} + +static unsigned long +append_extra_args_unicode(void *data, unsigned long maxchars) +{ + char *p = data; + int i, appended=0; + unsigned long usedchars=0; + if (!data) return 0; + + + for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) { + p += efichar_from_char((efi_char16_t *)p, opts.argv[i], + maxchars-usedchars); + usedchars = efichar_strsize(data) - sizeof(efi_char16_t); + appended=1; + + /* Put a space between args */ + if (i < (opts.argc-1)) { + p += efichar_from_char((efi_char16_t *)p, " ", + maxchars-usedchars); + usedchars = efichar_strsize(data) - + sizeof(efi_char16_t); + } + } + + if (appended) return efichar_strsize( (efi_char16_t *)data ); + return 0; +} + + +static unsigned long +append_extra_args(void *data, unsigned long maxchars) +{ + if (opts.unicode) + return append_extra_args_unicode(data, maxchars); + else + return append_extra_args_ascii(data, maxchars); +} + + + +int +make_linux_efi_variable(efi_variable_t *var, + unsigned int free_number) +{ + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char buffer[16]; + unsigned char *optional_data=NULL; + unsigned long load_option_size = 0, opt_data_size=0; + + memset(buffer, 0, sizeof(buffer)); + + /* VariableName needs to be BootXXXX */ + sprintf(buffer, "Boot%04x", free_number); + + efichar_from_char(var->VariableName, buffer, 1024); + + memcpy(&(var->VendorGuid), &guid, sizeof(guid)); + var->Attributes = + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + + /* Set Data[] and DataSize */ + + load_option_size = make_linux_load_option(var->Data); + + if (!load_option_size) return 0; + + /* Set OptionalData (passed as binary to the called app) */ + optional_data = var->Data + load_option_size; + opt_data_size = append_extra_args(optional_data, + sizeof(var->Data) - load_option_size); + var->DataSize = load_option_size + opt_data_size; + return var->DataSize; +} diff --git a/src/lib/efichar.c b/src/lib/efichar.c new file mode 100644 index 0000000..831c759 --- /dev/null +++ b/src/lib/efichar.c @@ -0,0 +1,130 @@ +/* + efichar.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Best as I can tell, efi_char16_t characters are unicode characters, + but are encoded in little-endian UCS-2 + http://www.cl.cam.ac.uk/~mgk25/unicode.html. + libunicode expects characters to be encoded in UTF-8, so I can't use that. + Therefore, we need a UCS-2 library. +*/ + +#include +#include +#include "efi.h" +#include "efichar.h" + +int +efichar_char_strcmp(const char *s1, const efi_char16_t *s2) +{ + int i, rc; + char *buffer; + int s2_len = efichar_strlen(s2, -1); + + buffer = malloc(s2_len+1); + if (!buffer) return -1; + memset(buffer, 0, s2_len+1); + + for (i=0; i<(s2_len); i++) { + buffer[i] = s2[i] & 0xFF; + } + buffer[i] = '\0'; + rc = strcmp(s1, buffer); + free(buffer); + return rc; +} + +int +efichar_strcmp(const efi_char16_t *s1, const efi_char16_t *s2) +{ + int i; + int s1_len = efichar_strlen(s1, -1); + int s2_len = efichar_strlen(s2, -1); + + + for (i=0; i < s1_len && i < s2_len; i++) { + if (s1[i] < s2[i]) return -1; + if (s1[i] > s2[i]) return 1; + + } + /* Hit the end of one string */ + if (i == s1_len && i != s2_len) return -1; + if (i != s1_len && i == s2_len) return 1; + return 0; +} + +unsigned long +efichar_from_char(efi_char16_t *dest, const char *src, size_t dest_len) +{ + int i, src_len = strlen(src); + for (i=0; i < src_len && i < (dest_len/sizeof(*dest)) - 1; i++) { + dest[i] = src[i]; + } + dest[i] = 0; + return i * sizeof(*dest); +} + +unsigned long +efichar_to_char(char *dest, const efi_char16_t *src, size_t dest_len) +{ + int i, src_len = efichar_strlen(src, -1); + for (i=0; i < src_len && i < (dest_len/sizeof(*dest)) - 1; i++) { + dest[i] = src[i]; + } + dest[i] = 0; + return i; +} + +int +efichar_strlen(const efi_char16_t *p, int max) +{ + int len=0; + const efi_char16_t *start = p; + + if (!p || !*p) return 0; + + while ((max < 0 || p - start < max) && *(p+len)) + { + ++len; + } + return len; +} + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +int +efichar_strncpy(efi_char16_t *dest, const efi_char16_t *src, int max) +{ + int i; + int src_len = efichar_strlen(src, max); + + for (i=0; i < MIN(max,src_len); i++) + { + dest[i] = src[i]; + } + dest[i] = 0; + return i; +} + +int +efichar_strsize(const efi_char16_t *p) +{ + return (efichar_strlen(p, -1) + 1) * sizeof(efi_char16_t); +} diff --git a/src/lib/gpt.c b/src/lib/gpt.c new file mode 100644 index 0000000..e4985d1 --- /dev/null +++ b/src/lib/gpt.c @@ -0,0 +1,652 @@ +/* + gpt.[ch] + + Copyright (C) 2000-2001 Dell Computer Corporation + + EFI GUID Partition Table handling + Per Intel EFI Specification v1.02 + http://developer.intel.com/technology/efi/efi.htm + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crc32.h" +#include "disk.h" +#include "gpt.h" +#include "efibootmgr.h" + +#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#define BLKSSZGET _IO(0x12,104) /* get block device sector size */ +#define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ + +struct blkdev_ioctl_param { + unsigned int block; + size_t content_length; + char * block_contents; +}; + +/** + * efi_crc32() - EFI version of crc32 function + * @buf: buffer to calculate crc32 of + * @len - length of buf + * + * Description: Returns EFI-style CRC32 value for @buf + * + * This function uses the little endian Ethernet polynomial + * but seeds the function with ~0, and xor's with ~0 at the end. + * Note, the EFI Specification, v1.02, has a reference to + * Dr. Dobbs Journal, May 1994 (actually it's in May 1992). + */ +static inline uint32_t +efi_crc32(const void *buf, unsigned long len) +{ + return (crc32(buf, len, ~0L) ^ ~0L); +} + +/** + * is_pmbr_valid(): test Protective MBR for validity + * @mbr: pointer to a legacy mbr structure + * + * Description: Returns 1 if PMBR is valid, 0 otherwise. + * Validity depends on two things: + * 1) MSDOS signature is in the last two bytes of the MBR + * 2) One partition of type 0xEE is found + */ +static int +is_pmbr_valid(legacy_mbr *mbr) +{ + int i, found = 0, signature = 0; + if (!mbr) + return 0; + signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE); + for (i = 0; signature && i < 4; i++) { + if (mbr->partition[i].os_type == + EFI_PMBR_OSTYPE_EFI_GPT) { + found = 1; + break; + } + } + return (signature && found); +} + +/** + * kernel_has_blkgetsize64() + * + * Returns: 0 on false, 1 on true + * True means kernel is 2.4.x, x>=18, or + * is 2.5.x, x>4, or + * is > 2.5 + */ +static int +kernel_has_blkgetsize64(void) +{ + int major=0, minor=0, patch=0, parsed; + int rc; + struct utsname u; + + memset(&u, 0, sizeof(u)); + rc = uname(&u); + if (rc) return 0; + + parsed = sscanf(u.release, "%d.%d.%d", &major, &minor, &patch); + if (parsed < 3) return 0; + if (major > 2) return 1; + if (major == 2 && minor > 5) return 1; + if (major == 2 && minor == 5 && patch >= 4) return 1; + if (major == 2 && minor == 4 && patch >= 18) return 1; + return 0; +} + + +/************************************************************ + * _get_num_sectors + * Requires: + * - filedes is an open file descriptor, suitable for reading + * Modifies: nothing + * Returns: + * Last LBA value on success + * 0 on error + * + * Try getting BLKGETSIZE64 and BLKSSZGET first, + * then BLKGETSIZE if necessary. + * Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64 + * which returns the number of 512-byte sectors, not the size of + * the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3. + ************************************************************/ +static uint64_t +_get_num_sectors(int filedes) +{ + unsigned long sectors=0; + uint64_t bytes=0; + int rc; + if (kernel_has_blkgetsize64()) { + rc = ioctl(filedes, BLKGETSIZE64, &bytes); + if (!rc) + return bytes / get_sector_size(filedes); + } + + rc = ioctl(filedes, BLKGETSIZE, §ors); + if (rc) + return 0; + + return sectors; +} + +/************************************************************ + * last_lba(): return number of last logical block of device + * + * @fd + * + * Description: returns Last LBA value on success, 0 on error. + * Notes: The value st_blocks gives the size of the file + * in 512-byte blocks, which is OK if + * EFI_BLOCK_SIZE_SHIFT == 9. + ************************************************************/ + +static uint64_t +last_lba(int filedes) +{ + int rc; + uint64_t sectors = 0; + struct stat s; + memset(&s, 0, sizeof (s)); + rc = fstat(filedes, &s); + if (rc == -1) { + fprintf(stderr, "last_lba() could not stat: %s\n", + strerror(errno)); + return 0; + } + + if (S_ISBLK(s.st_mode)) { + sectors = _get_num_sectors(filedes); + } else { + fprintf(stderr, + "last_lba(): I don't know how to handle files with mode %x\n", + s.st_mode); + sectors = 1; + } + + return sectors - 1; +} + + +static ssize_t +read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count) +{ + int rc; + struct blkdev_ioctl_param ioctl_param; + + if (!buffer) return 0; + + ioctl_param.block = 0; /* read the last sector */ + ioctl_param.content_length = count; + ioctl_param.block_contents = buffer; + + rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param); + if (rc == -1) perror("read failed"); + + return !rc; +} + +static ssize_t +read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) +{ + int sector_size = get_sector_size(fd); + off_t offset = lba * sector_size; + ssize_t bytesread; + void *aligned; + void *unaligned; + + if (bytes % sector_size) + return EINVAL; + + unaligned = malloc(bytes+sector_size-1); + aligned = (void *) + (((unsigned long)unaligned + sector_size - 1) & + ~(unsigned long)(sector_size-1)); + memset(aligned, 0, bytes); + + + lseek(fd, offset, SEEK_SET); + bytesread = read(fd, aligned, bytes); + memcpy(buffer, aligned, bytesread); + + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + This is only used by gpt.c, and only to read + one sector, so we don't have to be fancy. + */ + if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) { + bytesread = read_lastoddsector(fd, lba, buffer, bytes); + } + return bytesread; +} + +/** + * alloc_read_gpt_entries(): reads partition entries from disk + * @fd is an open file descriptor to the whole disk + * @gpt is a buffer into which the GPT will be put + * Description: Returns ptes on success, NULL on error. + * Allocates space for PTEs based on information found in @gpt. + * Notes: remember to free pte when you're done! + */ +static gpt_entry * +alloc_read_gpt_entries(int fd, gpt_header * gpt) +{ + gpt_entry *pte; + size_t count = __le32_to_cpu(gpt->num_partition_entries) * + __le32_to_cpu(gpt->sizeof_partition_entry); + + if (!count) return NULL; + + pte = (gpt_entry *)malloc(count); + if (!pte) + return NULL; + memset(pte, 0, count); + + if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte, + count)) { + free(pte); + return NULL; + } + return pte; +} + +/** + * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk + * @fd is an open file descriptor to the whole disk + * @lba is the Logical Block Address of the partition table + * + * Description: returns GPT header on success, NULL on error. Allocates + * and fills a GPT header starting at @ from @bdev. + * Note: remember to free gpt when finished with it. + */ +static gpt_header * +alloc_read_gpt_header(int fd, uint64_t lba) +{ + gpt_header *gpt; + gpt = (gpt_header *) + malloc(sizeof (gpt_header)); + if (!gpt) + return NULL; + memset(gpt, 0, sizeof (*gpt)); + if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) { + free(gpt); + return NULL; + } + + return gpt; +} + +/** + * is_gpt_valid() - tests one GPT header and PTEs for validity + * @fd is an open file descriptor to the whole disk + * @lba is the logical block address of the GPT header to test + * @gpt is a GPT header ptr, filled on return. + * @ptes is a PTEs ptr, filled on return. + * + * Description: returns 1 if valid, 0 on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + */ +static int +is_gpt_valid(int fd, uint64_t lba, + gpt_header ** gpt, gpt_entry ** ptes) +{ + int rc = 0; /* default to not valid */ + uint32_t crc, origcrc; + + if (!gpt || !ptes) + return 0; + if (!(*gpt = alloc_read_gpt_header(fd, lba))) + return 0; + + /* Check the GUID Partition Table signature */ + if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) { + /* + printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n", + __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE); + */ + free(*gpt); + *gpt = NULL; + return rc; + } + + /* Check the GUID Partition Table Header CRC */ + origcrc = __le32_to_cpu((*gpt)->header_crc32); + (*gpt)->header_crc32 = 0; + crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size)); + if (crc != origcrc) { + // printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc); + (*gpt)->header_crc32 = __cpu_to_le32(origcrc); + free(*gpt); + *gpt = NULL; + return 0; + } + (*gpt)->header_crc32 = __cpu_to_le32(origcrc); + + /* Check that the my_lba entry points to the LBA + * that contains the GPT we read */ + if (__le64_to_cpu((*gpt)->my_lba) != lba) { + // printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n", __le64_to_cpu((*gpt)->my_lba), lba); + free(*gpt); + *gpt = NULL; + return 0; + } + + if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) { + free(*gpt); + *gpt = NULL; + return 0; + } + + /* Check the GUID Partition Entry Array CRC */ + crc = efi_crc32(*ptes, + __le32_to_cpu((*gpt)->num_partition_entries) * + __le32_to_cpu((*gpt)->sizeof_partition_entry)); + if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) { + // printf("GUID Partitition Entry Array CRC check failed.\n"); + free(*gpt); + *gpt = NULL; + free(*ptes); + *ptes = NULL; + return 0; + } + + /* We're done, all's well */ + return 1; +} +/** + * compare_gpts() - Search disk for valid GPT headers and PTEs + * @pgpt is the primary GPT header + * @agpt is the alternate GPT header + * @lastlba is the last LBA number + * Description: Returns nothing. Sanity checks pgpt and agpt fields + * and prints warnings on discrepancies. + * + */ +static void +compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba) +{ + int error_found = 0; + if (!pgpt || !agpt) + return; + if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) { + fprintf(stderr, + "GPT:Primary header LBA != Alt. header alternate_lba\n"); + fprintf(stderr, "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n", + __le64_to_cpu(pgpt->my_lba), + __le64_to_cpu(agpt->alternate_lba)); + error_found++; + } + if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) { + fprintf(stderr, + "GPT:Primary header alternate_lba != Alt. header my_lba\n"); + fprintf(stderr, "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n", + __le64_to_cpu(pgpt->alternate_lba), + __le64_to_cpu(agpt->my_lba)); + error_found++; + } + if (__le64_to_cpu(pgpt->first_usable_lba) != + __le64_to_cpu(agpt->first_usable_lba)) { + fprintf(stderr, "GPT:first_usable_lbas don't match.\n"); + fprintf(stderr, "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n", + __le64_to_cpu(pgpt->first_usable_lba), + __le64_to_cpu(agpt->first_usable_lba)); + error_found++; + } + if (__le64_to_cpu(pgpt->last_usable_lba) != + __le64_to_cpu(agpt->last_usable_lba)) { + fprintf(stderr, "GPT:last_usable_lbas don't match.\n"); + fprintf(stderr, "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n", + __le64_to_cpu(pgpt->last_usable_lba), + __le64_to_cpu(agpt->last_usable_lba)); + error_found++; + } + if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) { + fprintf(stderr, "GPT:disk_guids don't match.\n"); + error_found++; + } + if (__le32_to_cpu(pgpt->num_partition_entries) != + __le32_to_cpu(agpt->num_partition_entries)) { + fprintf(stderr, "GPT:num_partition_entries don't match: " + "0x%x != 0x%x\n", + __le32_to_cpu(pgpt->num_partition_entries), + __le32_to_cpu(agpt->num_partition_entries)); + error_found++; + } + if (__le32_to_cpu(pgpt->sizeof_partition_entry) != + __le32_to_cpu(agpt->sizeof_partition_entry)) { + fprintf(stderr, + "GPT:sizeof_partition_entry values don't match: " + "0x%x != 0x%x\n", + __le32_to_cpu(pgpt->sizeof_partition_entry), + __le32_to_cpu(agpt->sizeof_partition_entry)); + error_found++; + } + if (__le32_to_cpu(pgpt->partition_entry_array_crc32) != + __le32_to_cpu(agpt->partition_entry_array_crc32)) { + fprintf(stderr, + "GPT:partition_entry_array_crc32 values don't match: " + "0x%x != 0x%x\n", + __le32_to_cpu(pgpt->partition_entry_array_crc32), + __le32_to_cpu(agpt->partition_entry_array_crc32)); + error_found++; + } + if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) { + fprintf(stderr, + "GPT:Primary header thinks Alt. header is not at the end of the disk.\n"); + fprintf(stderr, "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n", + __le64_to_cpu(pgpt->alternate_lba), lastlba); + error_found++; + } + + if (__le64_to_cpu(agpt->my_lba) != lastlba) { + fprintf(stderr, + "GPT:Alternate GPT header not at the end of the disk.\n"); + fprintf(stderr, "GPT:0x%" PRIx64 " != 0x%" PRIx64 "\n", + __le64_to_cpu(agpt->my_lba), lastlba); + error_found++; + } + + if (error_found) + fprintf(stderr, + "GPT: Use GNU Parted to correct GPT errors.\n"); + return; +} + +/** + * find_valid_gpt() - Search disk for valid GPT headers and PTEs + * @fd is an open file descriptor to the whole disk + * @gpt is a GPT header ptr, filled on return. + * @ptes is a PTEs ptr, filled on return. + * Description: Returns 1 if valid, 0 on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + * Validity depends on finding either the Primary GPT header and PTEs valid, + * or the Alternate GPT header and PTEs valid, and the PMBR valid. + */ +static int +find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) +{ + int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; + gpt_header *pgpt = NULL, *agpt = NULL; + gpt_entry *pptes = NULL, *aptes = NULL; + legacy_mbr *legacymbr = NULL; + uint64_t lastlba; + if (!gpt || !ptes) + return 0; + + lastlba = last_lba(fd); + good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, + &pgpt, &pptes); + if (good_pgpt) { + good_agpt = is_gpt_valid(fd, + __le64_to_cpu(pgpt->alternate_lba), + &agpt, &aptes); + if (!good_agpt) { + good_agpt = is_gpt_valid(fd, lastlba, + &agpt, &aptes); + } + } + else { + good_agpt = is_gpt_valid(fd, lastlba, + &agpt, &aptes); + } + + /* The obviously unsuccessful case */ + if (!good_pgpt && !good_agpt) { + goto fail; + } + + /* This will be added to the EFI Spec. per Intel after v1.02. */ + legacymbr = malloc(sizeof (*legacymbr)); + if (legacymbr) { + memset(legacymbr, 0, sizeof (*legacymbr)); + read_lba(fd, 0, (uint8_t *) legacymbr, + sizeof (*legacymbr)); + good_pmbr = is_pmbr_valid(legacymbr); + free(legacymbr); + legacymbr=NULL; + } + + /* Failure due to bad PMBR */ + if ((good_pgpt || good_agpt) && !good_pmbr && !opts.forcegpt) { + fprintf(stderr, + " Warning: Disk has a valid GPT signature " + "but invalid PMBR.\n" + " Assuming this disk is *not* a GPT disk anymore.\n" + " Use gpt kernel option to override. " + "Use GNU Parted to correct disk.\n"); + goto fail; + } + + /* Would fail due to bad PMBR, but force GPT anyhow */ + if ((good_pgpt || good_agpt) && !good_pmbr && opts.forcegpt) { + fprintf(stderr, + " Warning: Disk has a valid GPT signature but " + "invalid PMBR.\n" + " Use GNU Parted to correct disk.\n" + " gpt option taken, disk treated as GPT.\n"); + } + + compare_gpts(pgpt, agpt, lastlba); + + /* The good cases */ + if (good_pgpt && (good_pmbr || opts.forcegpt)) { + *gpt = pgpt; + *ptes = pptes; + if (agpt) { free(agpt); agpt = NULL; } + if (aptes) { free(aptes); aptes = NULL; } + if (!good_agpt) { + fprintf(stderr, + "Alternate GPT is invalid, " + "using primary GPT.\n"); + } + return 1; + } + else if (good_agpt && (good_pmbr || opts.forcegpt)) { + *gpt = agpt; + *ptes = aptes; + if (pgpt) { free(pgpt); pgpt = NULL; } + if (pptes) { free(pptes); pptes = NULL; } + fprintf(stderr, + "Primary GPT is invalid, using alternate GPT.\n"); + return 1; + } + + fail: + if (pgpt) { free(pgpt); pgpt=NULL; } + if (agpt) { free(agpt); agpt=NULL; } + if (pptes) { free(pptes); pptes=NULL; } + if (aptes) { free(aptes); aptes=NULL; } + *gpt = NULL; + *ptes = NULL; + return 0; +} + + +/************************************************************ + * gpt_disk_get_partition_info() + * Requires: + * - open file descriptor fd + * - start, size, signature, mbr_type, signature_type + * Modifies: all these + * Returns: + * 0 on success + * non-zero on failure + * + ************************************************************/ +int +gpt_disk_get_partition_info(int fd, + uint32_t num, + uint64_t * start, uint64_t * size, + char *signature, + uint8_t * mbr_type, uint8_t * signature_type) +{ + gpt_header *gpt = NULL; + gpt_entry *ptes = NULL, *p; + + if (!find_valid_gpt(fd, &gpt, &ptes)) + return 1; + + *mbr_type = 0x02; + *signature_type = 0x02; + + if (num > 0 && num <= __le32_to_cpu(gpt->num_partition_entries)) { + p = &ptes[num - 1]; + *start = __le64_to_cpu(p->starting_lba); + *size = __le64_to_cpu(p->ending_lba) - + __le64_to_cpu(p->starting_lba) + 1; + memcpy(signature, &p->unique_partition_guid, + sizeof (p->unique_partition_guid)); + } else { + *start = 0; + *size = last_lba(fd) + 1; + memcpy(signature, &gpt->disk_guid, sizeof (gpt->disk_guid)); + } + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/src/lib/module.mk b/src/lib/module.mk new file mode 100644 index 0000000..a0c62b7 --- /dev/null +++ b/src/lib/module.mk @@ -0,0 +1,3 @@ +CFLAGS += -Isrc/lib + +CLEANLIST += src/lib/*.o diff --git a/src/lib/scsi_ioctls.c b/src/lib/scsi_ioctls.c new file mode 100644 index 0000000..c19cef3 --- /dev/null +++ b/src/lib/scsi_ioctls.c @@ -0,0 +1,105 @@ +/* + scsi_ioctls.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "scsi_ioctls.h" + +int +idlun_to_components (Scsi_Idlun *idlun, + unsigned char *host, + unsigned char *channel, + unsigned char *id, + unsigned char *lun) +{ + if (!idlun || !host || !channel || !id || !lun) return 1; + + *host = (idlun->dev_id >> 24) & 0xFF; + *channel = (idlun->dev_id >> 16) & 0xFF; + *id = (idlun->dev_id ) & 0xFF; + *lun = (idlun->dev_id >> 8) & 0xFF; + return 0; +} + + +inline int +get_scsi_idlun(int fd, Scsi_Idlun *idlun) +{ + return ioctl(fd, SCSI_IOCTL_GET_IDLUN, idlun); +} + +inline int +get_scsi_pci(int fd, char *slot_name) +{ + return ioctl(fd, SCSI_IOCTL_GET_PCI, slot_name); +} + + + +#ifdef SCSI_IOCTLS_EXE +static void +usage(char **argv) +{ + printf("Usage: %s /dev/sdX where sdX is a SCSI device node.\n", + argv[0]); +} + +int main(int argc, char **argv) +{ + Scsi_Idlun idlun; + char slot_name[8]; + int fd = 0, rc = 0; + + memset(&idlun, 0, sizeof(idlun)); + + if (argc < 2) {usage(argv); exit(1);} + + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + perror("Unable to open file"); + exit(1); + } + + rc = get_scsi_pci(fd, slot_name); + if (rc) { + perror("Unable to get_scsi_pci()"); + } + rc = get_scsi_idlun(fd, &idlun); + if (rc) { + perror("Unable to get_scsi_idlun()"); + } + + printf("Device: %s\n", argv[1]); + printf("PCI: %s\n", slot_name); + + printf("SCSI: host %d channel %d id %d lun %d, unique ID %x\n", + (idlun.dev_id >> 24) & 0xFF, // host + (idlun.dev_id >> 16) & 0xFF, // channel + idlun.dev_id & 0xFF, // id + (idlun.dev_id >> 8) & 0xFF, // lun + idlun.host_unique_id); + + return 0; +} +#endif diff --git a/src/lib/unparse_path.c b/src/lib/unparse_path.c new file mode 100644 index 0000000..3c28f1c --- /dev/null +++ b/src/lib/unparse_path.c @@ -0,0 +1,550 @@ +/* + unparse_path.[ch] + + Copyright (C) 2001 Dell Computer Corporation + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* For PRIx64 */ +#define __STDC_FORMAT_MACROS + +#include +#include +#include +#include +#include +#include + +#include "efi.h" +#include "unparse_path.h" +#include "efichar.h" + + + + +void +dump_raw_data(void *data, uint64_t length) +{ + char buffer1[80], buffer2[80], *b1, *b2, c; + unsigned char *p = data; + unsigned long column=0; + uint64_t length_printed = 0; + const char maxcolumn = 16; + while (length_printed < length) { + b1 = buffer1; + b2 = buffer2; + for (column = 0; + column < maxcolumn && length_printed < length; + column ++) { + b1 += sprintf(b1, "%02x ",(unsigned int) *p); + if (*p < 32 || *p > 126) c = '.'; + else c = *p; + b2 += sprintf(b2, "%c", c); + p++; + length_printed++; + } + /* pad out the line */ + for (; column < maxcolumn; column++) + { + b1 += sprintf(b1, " "); + b2 += sprintf(b2, " "); + } + + printf("%s\t%s\n", buffer1, buffer2); + } +} + + + +unsigned long +unparse_raw(char *buffer, uint8_t *p, uint64_t length) +{ + uint64_t i; unsigned char c; + char *q = buffer; + for (i=0; i 127) c = '.'; + q += sprintf(q, "%02x", c); + } + return q - buffer; +} + +unsigned long +unparse_raw_text(char *buffer, uint8_t *p, uint64_t length) +{ + uint64_t i; unsigned char c; + char *q = buffer; + for (i=0; i 127) c = '.'; + q += sprintf(q, "%c", c); + } + return q - buffer; +} + +static int +unparse_ipv4_port(char *buffer, uint32_t ipaddr, uint16_t port) +{ + unsigned char *ip; +// ipaddr = nltoh(ipaddr); +// port = nstoh(port); + ip = (unsigned char *)&ipaddr; + return sprintf(buffer, "%hhu.%hhu.%hhu.%hhu:%hu", + ip[0], ip[1], ip[2], ip[3], + port); +} + + +static int +unparse_acpi_path(char *buffer, EFI_DEVICE_PATH *path) +{ + ACPI_DEVICE_PATH *acpi = (ACPI_DEVICE_PATH *)path; + + switch (path->subtype) { + case 1: + return sprintf(buffer, "ACPI(%x,%x)", acpi->_HID, acpi->_UID); + break; + default: + return unparse_raw(buffer, (uint8_t *)path, path->length); + break; + } + return 0; +} + +static int +unparse_vendor_path(char *buffer, VENDOR_DEVICE_PATH *path) +{ + char text_guid[40], *p = buffer, *q = (uint8_t *)path + 20; + efi_guid_unparse(&path->vendor_guid, text_guid); + p += sprintf(p, "Vendor(%s,", text_guid); + p += unparse_raw(p, q, path->length - 20); + p += sprintf(p, ")"); + return p - buffer; +} + +static int +unparse_hardware_path(char *buffer, EFI_DEVICE_PATH *path) +{ + PCI_DEVICE_PATH *pci = (PCI_DEVICE_PATH *)path; + PCCARD_DEVICE_PATH *pccard = (PCCARD_DEVICE_PATH *)path; + MEMORY_MAPPED_DEVICE_PATH *mm = (MEMORY_MAPPED_DEVICE_PATH *)path; + CONTROLLER_DEVICE_PATH *ctlr = (CONTROLLER_DEVICE_PATH *)path; + + switch (path->subtype) { + case 1: + return sprintf(buffer, "PCI(%x,%x)", + pci->device, pci->function); + break; + case 2: + return sprintf(buffer, "PCCARD(%x)", pccard->socket); + break; + case 3: + return sprintf(buffer, "MM(%x,%" PRIx64 ",%" PRIx64 ")", + mm->memory_type, + mm->start, mm->end); + break; + case 4: + return unparse_vendor_path(buffer, (VENDOR_DEVICE_PATH *)path); + break; + + case 5: + return sprintf(buffer, "Controller(%x)", ctlr->controller); + break; + + default: + return unparse_raw(buffer, (uint8_t *)path, path->length); + } + return 0; +} + + +static int +unparse_messaging_path(char *buffer, EFI_DEVICE_PATH *path) +{ + ATAPI_DEVICE_PATH *atapi = (ATAPI_DEVICE_PATH *)path; + SCSI_DEVICE_PATH *scsi = (SCSI_DEVICE_PATH *)path; + FIBRE_CHANNEL_DEVICE_PATH *fc = (FIBRE_CHANNEL_DEVICE_PATH *)path; + I1394_DEVICE_PATH *i1394 = (I1394_DEVICE_PATH *)path; + USB_DEVICE_PATH *usb = (USB_DEVICE_PATH *)path; + MAC_ADDR_DEVICE_PATH *mac = (MAC_ADDR_DEVICE_PATH *)path; + USB_CLASS_DEVICE_PATH *usbclass = (USB_CLASS_DEVICE_PATH *)path; + I2O_DEVICE_PATH *i2o = (I2O_DEVICE_PATH *)path; + IPv4_DEVICE_PATH *ipv4 = (IPv4_DEVICE_PATH *)path; +/* IPv6_DEVICE_PATH *ipv6 = (IPv6_DEVICE_PATH *)path; */ + char *p = buffer; + + switch (path->subtype) { + case 1: + return sprintf(buffer, "ATAPI(%x,%x,%x)", + atapi->primary_secondary, + atapi->slave_master, atapi->lun); + break; + case 2: + return sprintf(buffer, "SCSI(%x,%x)", scsi->id, scsi->lun); + break; + + case 3: + return sprintf(buffer, "FC(%" PRIx64 ",%" PRIx64 ")", fc->wwn, fc->lun); + break; + case 4: + return sprintf(buffer, "1394(%" PRIx64 ")", i1394->guid); + break; + case 5: + return sprintf(buffer, "USB(%x,%x)", usb->port, usb->endpoint); + break; + case 6: + return sprintf(buffer, "I2O(%x)", i2o->tid); + break; + case 11: + p += sprintf(p, "MAC("); + p += unparse_raw(p, mac->macaddr, 6); + p += sprintf(p, ",%hhx)", mac->iftype); + return (int) (p - buffer); + break; + case 12: + p += sprintf(p, "IPv4("); + p += unparse_ipv4_port(p, ipv4->local_ip, ipv4->local_port); + p += sprintf(p, "<->"); + p += unparse_ipv4_port(p, ipv4->remote_ip, ipv4->remote_port); + p += sprintf(p, ",%hx, %hhx", ipv4->protocol, ipv4->static_addr); + return (int) (p - buffer); + break; + + case 15: + return sprintf(buffer, "USBClass(%hx,%hx,%hhx,%hhx,%hhx)", + usbclass->vendor, usbclass->product, + usbclass->class, usbclass->subclass, + usbclass->protocol); + break; + default: + return unparse_raw(buffer, (uint8_t *)path, path->length); + break; + } + return 0; +} + +static int +unparse_media_hard_drive_path(char *buffer, EFI_DEVICE_PATH *path) +{ + HARDDRIVE_DEVICE_PATH *hd = (HARDDRIVE_DEVICE_PATH *)path; + char text_uuid[40], *sig=text_uuid; + + switch (hd->signature_type) { + case 0x00: + sprintf(sig, "None"); + break; + case 0x01: + sprintf(sig, "%08x", *(uint32_t *)hd->signature); + break; + case 0x02: /* GPT */ + efi_guid_unparse((efi_guid_t *)hd->signature, sig); + break; + default: + break; + } + + return sprintf(buffer, "HD(%x,%" PRIx64 ",%" PRIx64 ",%s)", + hd->part_num, hd->start, hd->size, sig); +} + + + +static int +unparse_media_path(char *buffer, EFI_DEVICE_PATH *path) +{ + + CDROM_DEVICE_PATH *cdrom = (CDROM_DEVICE_PATH *)path; + MEDIA_PROTOCOL_DEVICE_PATH *media = (MEDIA_PROTOCOL_DEVICE_PATH *)path; + FILE_PATH_DEVICE_PATH *file = (FILE_PATH_DEVICE_PATH *)path; + char text_guid[40], *p = buffer; + char file_name[80]; + memset(file_name, 0, sizeof(file_name)); + + switch (path->subtype) { + case 1: + return unparse_media_hard_drive_path(buffer, path); + break; + case 2: + return sprintf(buffer, "CD-ROM(%x,%" PRIx64 ",%" PRIx64 ")", + cdrom->boot_entry, cdrom->start, cdrom->size); + break; + case 3: + return unparse_vendor_path(buffer, (VENDOR_DEVICE_PATH *)path); + break; + case 4: + efichar_to_char(file_name, file->path_name, 80); + return sprintf(p, "File(%s)", file_name); + break; + case 5: + efi_guid_unparse(&media->guid, text_guid); + return sprintf(buffer, "Media(%s)", text_guid); + break; + default: + break; + } + return 0; +} + +static int +unparse_bios_path(char *buffer, EFI_DEVICE_PATH *path) +{ + BIOS_BOOT_SPEC_DEVICE_PATH *bios = (BIOS_BOOT_SPEC_DEVICE_PATH *)path; + char *p = buffer, *q = (uint8_t *)path + 8; + p += sprintf(p, "BIOS(%x,%x,", + bios->device_type, bios->status_flag); + p += unparse_raw(p, q, path->length - 8); + p += sprintf(p, ")"); + return p - buffer; +} + + +uint64_t +unparse_path(char *buffer, EFI_DEVICE_PATH *path, uint16_t pathsize) +{ + uint16_t parsed_length = 0; + char *p = buffer; + int exit_now = 0; + + while (parsed_length < pathsize && !exit_now) { + switch (path->type) { + case 0x01: + p += unparse_hardware_path(p, path); + break; + case 0x02: + p += unparse_acpi_path(p, path); + break; + case 0x03: + p += unparse_messaging_path(p, path); + break; + case 0x04: + p += unparse_media_path(p, path); + break; + case 0x05: + p += unparse_bios_path(p, path); + break; + case 0x7F: + exit_now = 1; + break; + case 0xFF: + exit_now = 1; + break; + default: + printf("\nwierd path"); + dump_raw_data(path, 4); + break; + } +// p += sprintf(p, "\\"); + parsed_length += path->length; + path = (EFI_DEVICE_PATH *) ((uint8_t *)path + path->length); + } + + return p - buffer; +} + + +#if 0 +static void +unparse_var(efi_variable_t *var) +{ + char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + + unparse_path(buffer, (EFI_DEVICE_PATH *)var->Data, var->DataSize); + printf("%s\n", buffer); +} + +static int +compare_hardware_path_pci(EFI_DEVICE_PATH *path, + int device, int func) +{ + uint8_t *p = ((void *)path) + OFFSET_OF(EFI_DEVICE_PATH, data); + uint8_t path_device, path_func; + + switch (path->subtype) { + case 1: + /* PCI */ + path_func = *(uint8_t *)p; + path_device = *(uint8_t *)(p+1); + + return !(path_func == func && path_device == device); + + break; + default: + break; + } + return 1; +} + +static int +compare_hardware_path_scsi(EFI_DEVICE_PATH *path, int id, int lun) +{ + uint8_t *p = ((void *)path) + OFFSET_OF(EFI_DEVICE_PATH, data); + uint16_t path_id, path_lun; + + switch (path->subtype) { + case 2: + /* SCSI */ + path_id = *(uint16_t *)p; + path_lun = *(uint16_t *)(p+2); + + return !(path_id == id && path_lun == lun); + break; + default: + break; + } + return 1; +} + +static int +compare_hardware_path_acpi(EFI_DEVICE_PATH *path, int bus) +{ + uint8_t *p = ((void *)path) + OFFSET_OF(EFI_DEVICE_PATH, data); + uint32_t _HID, _UID; + + switch (path->subtype) { + case 1: + /* ACPI */ + _HID = *(uint32_t *)p; + _UID = *(uint32_t *)(p+4); + + /* FIXME: Need to convert _HID and _UID to bus number */ + + return 0; + break; + default: + break; + } + return 1; +} + +static int +compare_media_path_harddrive(EFI_DEVICE_PATH *path, uint32_t num, + uint64_t start, uint64_t size) +{ + HARDDRIVE_DEVICE_PATH *p = (HARDDRIVE_DEVICE_PATH *)path; + + + switch (path->subtype) { + case 1: + /* Hard Drive */ + return !(p->part_num == num + && p->start == start + && p->size == size); + break; + default: + break; + } + return 1; +} + + + +int +compare_pci_scsi_disk_blk(efi_variable_t *var, + int bus, int device, int func, + int host, int channel, int id, int lun, + uint64_t start, uint64_t size) +{ + + EFI_DEVICE_PATH *path = (EFI_DEVICE_PATH *) var->Data; + uint64_t parsed_length = 0; + int exit_now = 0; + int rc = 0; + + while (parsed_length < var->DataSize && !exit_now && !rc) { + switch (path->type) { + case 0x01: + /* Hardware (PCI) */ + rc = compare_hardware_path_pci(path, device, func); + break; + case 0x02: + /* ACPI */ + rc = compare_hardware_path_acpi(path, bus); + break; + case 0x03: + /* Messaging (SCSI) */ + rc = compare_messaging_path_scsi(path, id, lun); + break; + case 0x04: + /* Media (Hard Drive) */ + rc = compare_media_path_harddrive(path, 0, + start, size); + break; + case 0x7F: + case 0xFF: + exit_now = 1; + break; + case 0x05: /* BIOS */ + default: + break; + } + parsed_length += path->length; + path = var->Data + parsed_length; + } + return rc; +} + +#endif + + + + + + + + + + + + +#ifdef UNPARSE_PATH +static void +usage(void) +{ + printf("Usage: dumppath filename\n"); + printf("\t where filename is a blkXXXX EFI variable from /proc/efi/vars\n"); +} + +int +main(int argc, char **argv) +{ + int fd = 0; + ssize_t size; + efi_variable_t var; + + if (argc == 1) { + usage(); + exit(-1); + } + + + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + perror("Failed to open file."); + exit(-1); + } + size = read(fd, &var, sizeof(var)); + if (size == -1 || size < sizeof(var)) { + perror("Failed to read file."); + close(fd); + exit(-1); + } + unparse_var(&var); + + + return 0; +} +#endif diff --git a/src/man/man8/efibootmgr.8 b/src/man/man8/efibootmgr.8 new file mode 100644 index 0000000..a2f6d49 --- /dev/null +++ b/src/man/man8/efibootmgr.8 @@ -0,0 +1,200 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng . +.TH "EFIBOOTMGR" "8" "11 August 2003" "" "" +.SH NAME +efibootmgr \- manipulate the EFI Boot Manager +.SH SYNOPSIS + +\fBefibootmgr\fR [ \fB-a\fR] [ \fB-A\fR] [ \fB-b \fIXXXX\fB\fR] [ \fB-B \fIXXXX\fB\fR] [ \fB-c\fR] [ \fB-d \fIDISK\fB\fR] [ \fB-e \fI1|3|-1\fB\fR] [ \fB-E \fINUM\fB\fR] [ \fB-g\fR] [ \fB-H \fIXXXX\fB\fR] [ \fB-i \fINAME\fB\fR] [ \fB-l \fINAME\fB\fR] [ \fB-L \fILABEL\fB\fR] [ \fB-n \fIXXXX\fB\fR] [ \fB-N\fR] [ \fB-o \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR\fI ...\fR] [ \fB-O\fR] [ \fB-p \fIPART\fB\fR] [ \fB-q\fR] [ \fB-t\fR] [ \fB-u\fR] [ \fB-U \fIXXXX\fB\fR] [ \fB-v\fR] [ \fB-V\fR] [ \fB-w\fR] + +.SH "DESCRIPTION" +.PP +\fBefibootmgr\fR is a userspace application used to +modify the Intel Extensible Firmware Interface (EFI) Boot Manager. This +application can create and destroy boot entries, change the boot order, +change the next running boot option, and more. +.PP +Details on the EFI Boot Manager are available from the EFI +Specification, v1.02 or later, available from: + +.sp +.RS +.B "Note:" +efibootmgr requires that the kernel support access to EFI +non-volatile variables (through \fI/proc/efi/vars\fR). +\fBmodprobe efivars\fR should do the trick. +.RE +.SH "OPTIONS" +.PP +The following is a list of options accepted by efibootmgr: +.TP +\fB-a | --active\fR +Sets bootnum active +.TP +\fB-A | --inactive\fR +Sets bootnum inactive +.TP +\fB-b | --bootnum \fIXXXX\fB\fR +Modify Boot\fIXXXX\fR (hex) +.TP +\fB-B | --delete-bootnum\fR +Delete bootnum (hex) +.TP +\fB-c | --create\fR +Create new variable bootnum and add to bootorder +.TP +\fB-d | --disk \fIDISK\fB\fR +The disk containing the loader (defaults to +\fI/dev/sda\fR) +.TP +\fB-e | --edd \fI1|3|-1\fB\fR +Force EDD 1.0 or 3.0 creation variables, or guess. +.TP +\fB-E | --device \fINUM\fB\fR +EDD 1.0 device number (defaults to 0x80) +.TP +\fB-g | --gpt\fR +Force disk with invalid PMBR to be treated as GPT +.TP +\fB-H | --acpi_hid \fIXXXX\fB\fR +set the ACPI HID (used with \fB-i\fR) +.TP +\fB-i | --iface \fINAME\fB\fR +create a netboot entry for the named interface +.TP +\fB-l | --loader \fINAME\fB\fR +Specify a loader (defaults to \fI\\\\elilo.efi\fR) +.TP +\fB-L | --label \fILABEL\fB\fR +Boot manager display label (defaults to "Linux") +.TP +\fB-n | --bootnext \fIXXXX\fB\fR +Set BootNext to XXXX (hex) +.TP +\fB-N | --delete-bootnext\fR +Delete BootNext +.TP +\fB-o | --bootorder \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR +Explicitly set BootOrder (hex) +.TP +\fB-O | --delete-bootorder\fR +Delete BootOrder +.TP +\fB-p | --part \fIPART\fB\fR +Partition number containing the bootloader (defaults to 1) +.TP +\fB-q | --quiet\fR +Quiet mode - supresses output. +.TP +\fB-t | --test \fIfilename\fB\fR +Don't write to NVRAM, write to \fIfilename\fR. +.TP +\fB-u | --unicode | --UCS-2 \fR +pass extra command line arguments as UCS-2 (default is +ASCII) +.TP +\fB-U | --acpi_uid \fIXXXX\fB\fR +set the ACPI UID (used with \fB-i\fR) +.TP +\fB-v | --verbose\fR +Verbose mode - prints additional information +.TP +\fB-V | --version\fR +Just print version string and exit. +.TP +\fB-w | --write-signature\fR +write unique signature to the MBR if needed +.SH "EXAMPLES" +.TP 1. +.SS "DISPLAYING THE CURRENT SETTINGS (MUST BE ROOT)." +.PP +[root@localhost ~]# efibootmgr +BootCurrent: 0004 +BootNext: 0003 +BootOrder: 0004,0000,0001,0002,0003 +Boot0000* Diskette Drive(device:0) +Boot0001* CD-ROM Drive(device:FF) +Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) +Boot0003* PXE Boot: MAC(00D0B7C15D91) +Boot0004* Linux +.PP +This shows: +.RS +.TP 0.2i +\(bu +BootCurrent - the boot entry used to start the currently +running system +.TP 0.2i +\(bu +BootOrder - the boot order as would appear in the boot manager. +The boot manager tries to boot the first active entry in this +list. If unsuccessful, it tries the next entry, and so on. +.TP 0.2i +\(bu +BootNext - the boot entry which is scheduled to be run on next +boot. This supercedes BootOrder for one boot only, and is +deleted by the boot manager after first use. This allows you +to change the next boot behavior without changing BootOrder. +.TP 0.2i +\(bu +Five boot entries (0000 - 0004), along with the active/inactive +flag (* means active) and the name displayed on the screen. +.RE +.TP 2. +.SS "CREATING A NEW BOOT OPTION" +.PP +An OS installer would call \fBefibootmgr -c\fR. +This assumes that \fI/boot/efi\fR is your EFI System +Partition, and is mounted at \fI/dev/sda1\fR. This +creates a new boot option, called "Linux", and puts it at the top of +the boot order list. Options may be passed to modify the default +behavior. The default OS Loader is \fIelilo.efi\fR. +.TP 3. +.SS "CHANGING THE BOOT ORDER" +.PP +Assuming the configuration in Example #1, +\fBefibootmgr -o 3,4\fR could be called to specify +PXE boot first, then Linux boot. +.TP 4. +.SS "CHANGING THE BOOT ORDER FOR THE NEXT BOOT ONLY" +.PP +Assuming the configuration in Example #1, +\fBefibootmgr -n 4\fR could be called to specify +that the Linux entry be taken on next boot. +.TP 5. +.SS "DELETING A BOOT OPTION" +.PP +Assuming the configuration in Example #1, +\fBefibootmgr -b 4 -B\fR could be called to delete +entry 4 and remove it from the BootOrder. +.TP 6. +.SS "CREATING NETWORK BOOT ENTRIES" +.PP +A system administrator wants to create a boot option to network +boot (PXE). Unfortunately, this requires knowing a little more +information about your system than can be easily found by +efibootmgr, so you've got to pass additional information - the ACPI +HID and UID values. These can generally be found by using the EFI +Boot Manager (in the EFI environment) to create a network boot +entry, then using efibootmgr to print it verbosely. Here's one example: +Boot003* Acpi(PNP0A03,0)/PCI(5|0)/Mac(00D0B7F9F510) \\ +ACPI(a0341d0,0)PCI(0,5)MAC(00d0b7f9f510,0) +In this case, the ACPI HID is "0A0341d0" and the UID is "0". +For the zx2000 gigE, the HID is "222F" and the UID is "500". +For the rx2000 gigE, the HID is "0002" and the UID is "100". +You create the boot entry with: +\fBefibootmgr -c -i eth0 -H 222F -U 500 -L netboot\fR +.SH "BUGS" +.PP +Please direct any bugs, features, patches, etc. to Matt Domsch +. +.SH "AUTHOR" +.PP +This man page was generated by dann frazier for the +Debian GNU/Linux operating system, but may be used by others. +.SH "SEE ALSO" +.PP +elilo(1) diff --git a/src/man/man8/efibootmgr.8.docbook b/src/man/man8/efibootmgr.8.docbook new file mode 100644 index 0000000..81c65b9 --- /dev/null +++ b/src/man/man8/efibootmgr.8.docbook @@ -0,0 +1,426 @@ + +dannf@debian.org, + and was based on an example constructed by Colin Watson + cjwatson@debian.org, which was based on a man page template + provided by Tom Christiansen tchrist@jhereg.perl.com + and a DocBook man page example by Craig Small + csmall@debian.org. + + The content was mostly taken from the efibootmgr README file in the + Debian package, which was written by Matt Domsch + Matt_Domsch@dell.com. + --> + + + dann"> + frazier"> + + 2003-08-11"> + + 8"> + dannf@debian.org"> + + EFIBOOTMGR"> + +]> + + + +
+ &manemail; +
+ + &manfirstname; + &mansurname; + + + 2002, 2003 + &manusername; + + &mandate; +
+ + &manucpackage; + + &mansection; + + + &manpackage; + + manipulate the EFI Boot Manager + + + + &manpackage; + + -a + -A + -b XXXX + -B XXXX + -c + -d DISK + -e 1|3|-1 + -E NUM + -g + -H XXXX + -i NAME + -l NAME + -L LABEL + -n XXXX + -N + -o XXXX,YYYY,ZZZZ + -O + -p PART + -q + -t + -u + -U XXXX + -v + -V + -w + + + + DESCRIPTION + + &manpackage; is a userspace application used to + modify the Intel Extensible Firmware Interface (EFI) Boot Manager. This + application can create and destroy boot entries, change the boot order, + change the next running boot option, and more. + + + Details on the EFI Boot Manager are available from the EFI + Specification, v1.02 or later, available from: + + + + + + &manpackage; requires that the kernel support access to EFI + non-volatile variables (through /proc/efi/vars). + modprobe efivars should do the trick. + + + + + OPTIONS + + The following is a list of options accepted by &manpackage;: + + + + | + + Sets bootnum active + + + + | + + Sets bootnum inactive + + + + | XXXX + + Modify BootXXXX (hex) + + + + | + + Delete bootnum (hex) + + + + | + + Create new variable bootnum and add to bootorder + + + + | DISK + + The disk containing the loader (defaults to + /dev/sda) + + + + | 1|3|-1 + + Force EDD 1.0 or 3.0 creation variables, or guess. + + + + | NUM + + EDD 1.0 device number (defaults to 0x80) + + + + | + + Force disk with invalid PMBR to be treated as GPT + + + + | XXXX + + set the ACPI HID (used with ) + + + + | NAME + + create a netboot entry for the named interface + + + + | NAME + + Specify a loader (defaults to \\elilo.efi) + + + + + | LABEL + + Boot manager display label (defaults to "Linux") + + + + | XXXX + + Set BootNext to XXXX (hex) + + + + | + + Delete BootNext + + + + | XXXX,YYYY,ZZZZ + + Explicitly set BootOrder (hex) + + + + | + + Delete BootOrder + + + + | PART + + Partition number containing the bootloader (defaults to 1) + + + + | + + Quiet mode - supresses output. + + + + | filename + + Don't write to NVRAM, write to filename. + + + + | | + + pass extra command line arguments as UCS-2 (default is + ASCII) + + + + | XXXX + + set the ACPI UID (used with ) + + + + | + + Verbose mode - prints additional information + + + + | + + Just print version string and exit. + + + + | + + write unique signature to the MBR if needed + + + + + + + EXAMPLES + + + + Displaying the current settings (must be root). + + + + [root@localhost ~]# efibootmgr + BootCurrent: 0004 + BootNext: 0003 + BootOrder: 0004,0000,0001,0002,0003 + Boot0000* Diskette Drive(device:0) + Boot0001* CD-ROM Drive(device:FF) + Boot0002* Hard Drive(Device:80)/HD(Part1,Sig00112233) + Boot0003* PXE Boot: MAC(00D0B7C15D91) + Boot0004* Linux + + + This shows: + + + BootCurrent - the boot entry used to start the currently + running system + + + + BootOrder - the boot order as would appear in the boot manager. + The boot manager tries to boot the first active entry in this + list. If unsuccessful, it tries the next entry, and so on. + + + + + BootNext - the boot entry which is scheduled to be run on next + boot. This supercedes BootOrder for one boot only, and is + deleted by the boot manager after first use. This allows you + to change the next boot behavior without changing BootOrder. + + + + + Five boot entries (0000 - 0004), along with the active/inactive + flag (* means active) and the name displayed on the screen. + + + + + + + + Creating a new boot option + + An OS installer would call &manpackage; -c. + This assumes that /boot/efi is your EFI System + Partition, and is mounted at /dev/sda1. This + creates a new boot option, called "Linux", and puts it at the top of + the boot order list. Options may be passed to modify the default + behavior. The default OS Loader is elilo.efi. + + + + + + Changing the Boot Order + + Assuming the configuration in Example #1, + &manpackage; -o 3,4 could be called to specify + PXE boot first, then Linux boot. + + + + + + Changing the Boot Order for the Next Boot Only + + Assuming the configuration in Example #1, + &manpackage; -n 4 could be called to specify + that the Linux entry be taken on next boot. + + + + + + Deleting a boot option + + Assuming the configuration in Example #1, + &manpackage; -b 4 -B could be called to delete + entry 4 and remove it from the BootOrder. + + + + + + Creating network boot entries + + A system administrator wants to create a boot option to network + boot (PXE). Unfortunately, this requires knowing a little more + information about your system than can be easily found by + efibootmgr, so you've got to pass additional information - the ACPI + HID and UID values. These can generally be found by using the EFI + Boot Manager (in the EFI environment) to create a network boot + entry, then using efibootmgr to print it verbosely. Here's one example: + + Boot003* Acpi(PNP0A03,0)/PCI(5|0)/Mac(00D0B7F9F510) \ + ACPI(a0341d0,0)PCI(0,5)MAC(00d0b7f9f510,0) + + In this case, the ACPI HID is "0A0341d0" and the UID is "0". + For the zx2000 gigE, the HID is "222F" and the UID is "500". + For the rx2000 gigE, the HID is "0002" and the UID is "100". + + You create the boot entry with: + efibootmgr -c -i eth0 -H 222F -U 500 -L netboot + + + + + + + + BUGS + + Please direct any bugs, features, patches, etc. to Matt Domsch + Matt_Domsch@dell.com. + + + + + AUTHOR + This man page was generated by dann frazier &manemail; for the + Debian GNU/Linux operating system, but may be used by others. + + + + SEE ALSO + + elilo(1) + +
+ + diff --git a/src/module.mk b/src/module.mk new file mode 100644 index 0000000..f56f5b4 --- /dev/null +++ b/src/module.mk @@ -0,0 +1,21 @@ +#add our stuff first... our children need to wait for us to finish +INSTALLDEPS += bindir_LIST + + MODULES := src/efibootmgr src/lib src/include + include $(patsubst %,%/module.mk,$(MODULES)) + + +# Common stuff to copy into the common directories +# Note that the stuff below bindir_LIST is all on one line... +bindir_LIST: + @if [ ! -z "$(bindir_TARGETS)" ]; then \ + echo "R-M-: %attr(0755,root,root) $(BINDIR)" ;\ + for file in $(bindir_TARGETS) ;\ + do \ + echo "-C--: $(BUILDDIR)/$$file $(BINDIR)/" ;\ + echo "R---: %attr(0755,root,root) $(BINDIR)/`basename $$file`" ;\ + done ;\ + echo ;\ + fi + + diff --git a/tools/install.pl b/tools/install.pl new file mode 100755 index 0000000..57c7c00 --- /dev/null +++ b/tools/install.pl @@ -0,0 +1,156 @@ +#!/usr/bin/perl -w + +#Pragmas + use strict; + +#Parse command line... + my ($copy, $link); + my $type = shift @ARGV; + if( $type =~ /^copy$/i ) { + $copy = 1; + } + if( $type =~ /^link$/i ) { + $link = 1; + } + +#Main program loop + open (RPMOUT, "> filelist-rpm") or die; + + while( <> ){ + chomp; + s/^\s*//; + s/\s*$//; + s/\s*#.*$//; + next if m/^$/; + + my $line = $_; + ($line =~ m/^R...:(.*)/) && print RPMOUT $1 . "\n"; + ($line =~ m/^..M.:(.*)/) && MakeDir( $1 ); + ($line =~ m/^.C..:(.*)/) && ($copy) && CopyFile( $1 ); + ($line =~ m/^.C..:(.*)/) && ($link) && LinkFile( $1 ); + ($line =~ m/^R...:(.*)/) && ChangeAttrs( $1 ); + } + +sub CopyFile { + my ($src, $dst) = ParseCLine( shift ); + + if( -d $dst ) { + $src =~ m|^.*/(.*)$|; + my $file = $1; + $dst .= "/" if ! ($dst=~m|/$|); + $dst .= $file; + } + if( -e $dst ) { + unlink $dst; + } + + open INPUT, "<", $src or goto out1; + open OUTPUT, ">", $dst or goto out2; + while(){ print OUTPUT $_ } + close OUTPUT; + + print "Installed File: $dst\n"; +out2: + close INPUT; +out1: + return; +} + +sub LinkFile { + my ($src, $dst) = ParseCLine( shift ); + if( -d $dst ) { + $src =~ m|^.*/(.*)$|; + my $file = $1; + $dst .= "/" if ! ($dst=~m|/$|); + $dst .= $file; + } + if( -e $dst ) { + unlink $dst; + } + symlink $src, $dst + or warn "Linking $src to $dst failed $!\n"; + print "Installed File: $dst\n"; +} + +sub ParseCLine { + my $line = shift; + $line =~ s/^\s*//; + $line =~ s/\s*$//; + $line =~ m/^(.*?)\s+(.*?)$/; + return ($1, $2); +} + +sub ParseRLine { + my $line = shift; + my @directives; + my @retval; + $line =~ s/^\s*//; + $line =~ s/\s*$//; + my @words = split( /\s+/, $line ); + foreach my $word (@words) { + if( $word =~ /^\%(.*)/ ) { + push @directives, $word; + } else { + push @retval, $word; + } + } + return \@retval, \@directives; +} + +sub MakeDir { + #R-M-: %attr(0755,root,ali) /opt/ali + my $line = shift; + + #$line =~ m/\%attr\((.{1,5}),(\w+),(\w+)\)\s+(.*)/; + my ($file_ref, $directive_ref) = ParseRLine( $line ); + + my $dir = $file_ref->[0]; + + if( ! -d $dir ) { + mkdir $dir + or warn "Make Dir: -->$dir<-- failed $!\n"; + } + print "Made Dir: $dir\n"; +} + +sub GetUID { + my $name = shift; + my (undef, undef, $uid, undef) = getpwnam( $name ) ; + $uid = defined($uid) ? $uid : -1; + return $uid; +} + +sub GetGID { + my $name = shift; + my (undef, undef, $gid, undef) = getgrnam( $name ) ; + $gid = defined($gid) ? $gid : -1; + return $gid; +} + +sub ChangeAttrs { + my $line = shift; + + my ($file_ref, $directive_ref) = ParseRLine( $line ); + my ($attr) = grep { /^\%attr/ } @$directive_ref; + $attr =~ m/\%attr\((.{1,5}),(\w+),(\w+)\)/; + my $perms = $1; + my $owner = $2; + my $group = $3; + my $file = $file_ref->[0]; + + my $uid = GetUID($owner); + my $gid = GetGID($group); + + chown $uid, $gid, $file; + chmod oct($perms), $file; +} + + + + + + + + + + -- 2.30.2