2
0
mirror of https://github.com/xcat2/xNBA.git synced 2025-08-29 06:18:16 +00:00

Init ipxe source code cloned fron ipxe.org at Oct 29th 2020

This commit is contained in:
cxhong
2020-11-10 10:27:09 -05:00
parent 6396e537fa
commit 7d0bfd6f4f
671 changed files with 188173 additions and 0 deletions

57
.travis.yml Normal file
View File

@@ -0,0 +1,57 @@
dist: trusty
sudo: false
git:
depth: false
language: c
cache: ccache
compiler:
- gcc
addons:
apt:
packages:
- binutils-dev
- liblzma-dev
- syslinux
- genisoimage
coverity_scan:
project:
name: "ipxe/ipxe"
version: $TRAVIS_COMMIT
build_command_prepend: "make -C src bin/deps"
build_command: "make -C src bin/blib.a"
branch_pattern: coverity_scan
env:
global:
- MAKEFLAGS="-j 4"
script:
- make -C src bin/blib.a
- make -C src bin/ipxe.pxe
- make -C src bin/ipxe.usb
- make -C src bin/ipxe.iso
- make -C src bin/8086100e.mrom
- make -C src bin-x86_64-pcbios/blib.a
- make -C src bin-x86_64-pcbios/ipxe.pxe
- make -C src bin-x86_64-pcbios/ipxe.usb
- make -C src bin-x86_64-pcbios/ipxe.iso
- make -C src bin-x86_64-pcbios/8086100e.mrom
- make -C src bin-x86_64-efi/blib.a
- make -C src bin-x86_64-efi/ipxe.efi
- make -C src bin-x86_64-efi/intel.efidrv
- make -C src bin-x86_64-efi/intel.efirom
- make -C src bin-i386-efi/blib.a
- make -C src bin-i386-efi/ipxe.efi
- make -C src bin-i386-efi/intel.efidrv
- make -C src bin-i386-efi/intel.efirom
- make -C src bin-x86_64-linux/blib.a
- make -C src bin-x86_64-linux/tap.linux
- make -C src bin-x86_64-linux/af_packet.linux
- make -C src bin-x86_64-linux/tests.linux
- ./src/bin-x86_64-linux/tests.linux

339
COPYING.GPLv2 Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

59
COPYING.UBDL Normal file
View File

@@ -0,0 +1,59 @@
UNMODIFIED BINARY DISTRIBUTION LICENCE
PREAMBLE
The GNU General Public License provides a legal guarantee that
software covered by it remains free (in the sense of freedom, not
price). It achieves this guarantee by imposing obligations on anyone
who chooses to distribute the software.
Some of these obligations may be seen as unnecessarily burdensome. In
particular, when the source code for the software is already publicly
and freely available, there is minimal value in imposing upon each
distributor the obligation to provide the complete source code (or an
equivalent written offer to provide the complete source code).
This Licence allows for the distribution of unmodified binaries built
from publicly available source code, without imposing the obligations
of the GNU General Public License upon anyone who chooses to
distribute only the unmodified binaries built from that source code.
The extra permissions granted by this Licence apply only to unmodified
binaries built from source code which has already been made available
to the public in accordance with the terms of the GNU General Public
Licence. Nothing in this Licence allows for the creation of
closed-source modified versions of the Program. Any modified versions
of the Program are subject to the usual terms and conditions of the
GNU General Public License.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
This Licence 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 Unmodified Binary Distribution Licence. All
terms used in the text of this Licence are to be interpreted as they
are used in version 2 of the GNU General Public License as published
by the Free Software Foundation.
If you have made this Program available to the public in both source
code and executable form in accordance with 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, then you are hereby granted an additional permission to use,
copy, and distribute the unmodified executable form of this Program
(the "Unmodified Binary") without restriction, including the right to
permit persons to whom the Unmodified Binary is furnished to do
likewise, subject to the following conditions:
- when started running, the Program must display an announcement which
includes the details of your existing publication of the Program
made in accordance with the terms of the GNU General Public License.
For example, the Program could display the URL of the publicly
available source code from which the Unmodified Binary was built.
- when exercising your right to grant permissions under this Licence,
you do not need to refer directly to the text of this Licence, but
you may not grant permissions beyond those granted to you by this
Licence.

29
contrib/coverity/model.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* Coverity modelling file
*
*/
typedef long off_t;
typedef void * userptr_t;
typedef long long time_t;
struct tm;
typedef unsigned short wchar_t;
typedef void mbstate_t;
struct digest_algorithm;
/* Inhibit use of built-in models for functions where Coverity's
* assumptions about the modelled function are incorrect for iPXE.
*/
char * strerror ( int errno ) {
}
void copy_from_user ( void *dest, userptr_t src, off_t src_off, size_t len ) {
}
time_t mktime ( struct tm *tm ) {
}
int getchar ( void ) {
}
size_t wcrtomb ( char *buf, wchar_t wc, mbstate_t *ps ) {
}
void hmac_init ( struct digest_algorithm *digest, void *digest_ctx,
void *key, size_t *key_len ) {
}

54
src/Makefile.efi Normal file
View File

@@ -0,0 +1,54 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# Enable stack protection if available
#
SPG_TEST = $(CC) -fstack-protector-strong -mstack-protector-guard=global \
-x c -c /dev/null -o /dev/null >/dev/null 2>&1
SPG_FLAGS := $(shell $(SPG_TEST) && $(ECHO) '-fstack-protector-strong ' \
'-mstack-protector-guard=global')
CFLAGS += $(SPG_FLAGS)
# The EFI linker script
#
LDSCRIPT = scripts/efi.lds
# Retain relocation information for elf2efi
#
LDFLAGS += -q -S
# Media types.
#
NON_AUTO_MEDIA += efi
NON_AUTO_MEDIA += efidrv
NON_AUTO_MEDIA += drv.efi
NON_AUTO_MEDIA += efirom
# Include SNP driver in the all-drivers build
#
DRIVERS_net += snp
# Rules for building EFI files
#
$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(ELF2EFI) --subsystem=10 $< $@
$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(ELF2EFI) --subsystem=11 $< $@
$(BIN)/%.drv.efi : $(BIN)/%.efidrv
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(CP) $< $@
$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@
$(BIN)/efidrv.cab : $(BIN)/alldrv.efis # $(ALL_drv.efi) is not yet defined
$(QM)$(ECHO) " [CAB] $@"
$(Q)$(LCAB) -n -q $(ALL_drv.efi) $@
$(BIN)/%.usb : $(BIN)/%.efi
$(QM)$(ECHO) " [GENEFIDSK] $@"
$(Q)bash util/genefidsk -o $@ -b $(EFI_BOOT_FILE) $<

12
src/arch/arm/Makefile Normal file
View File

@@ -0,0 +1,12 @@
# Assembler section type character
#
ASM_TCHAR := %
ASM_TCHAR_OPS := %%
# Include common ARM headers
#
INCDIRS += arch/arm/include
# ARM-specific directories containing source files
#
SRCDIRS += arch/arm/interface/efi

View File

@@ -0,0 +1,6 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# Include generic EFI Makefile
#
MAKEDEPS += Makefile.efi
include Makefile.efi

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/io.h>
#include <ipxe/arm_io.h>
/** @file
*
* iPXE I/O API for ARM
*
*/
/** An ARM I/O qword */
union arm32_io_qword {
uint64_t qword;
uint32_t dword[2];
};
/**
* Read 64-bit qword from memory-mapped device
*
* @v io_addr I/O address
* @ret data Value read
*
* This is not atomic for ARM32.
*/
static uint64_t arm32_readq ( volatile uint64_t *io_addr ) {
volatile union arm32_io_qword *ptr =
container_of ( io_addr, union arm32_io_qword, qword );
union arm32_io_qword tmp;
tmp.dword[0] = readl ( &ptr->dword[0] );
tmp.dword[1] = readl ( &ptr->dword[1] );
return tmp.qword;
}
/**
* Write 64-bit qword to memory-mapped device
*
* @v data Value to write
* @v io_addr I/O address
*
* This is not atomic for ARM32.
*/
static void arm32_writeq ( uint64_t data, volatile uint64_t *io_addr ) {
volatile union arm32_io_qword *ptr =
container_of ( io_addr, union arm32_io_qword, qword );
union arm32_io_qword tmp;
tmp.qword = data;
writel ( tmp.dword[0], &ptr->dword[0] );
writel ( tmp.dword[1], &ptr->dword[1] );
}
PROVIDE_IOAPI_INLINE ( arm, phys_to_bus );
PROVIDE_IOAPI_INLINE ( arm, bus_to_phys );
PROVIDE_IOAPI_INLINE ( arm, readb );
PROVIDE_IOAPI_INLINE ( arm, readw );
PROVIDE_IOAPI_INLINE ( arm, readl );
PROVIDE_IOAPI_INLINE ( arm, writeb );
PROVIDE_IOAPI_INLINE ( arm, writew );
PROVIDE_IOAPI_INLINE ( arm, writel );
PROVIDE_IOAPI_INLINE ( arm, iodelay );
PROVIDE_IOAPI_INLINE ( arm, mb );
#ifdef __aarch64__
PROVIDE_IOAPI_INLINE ( arm, readq );
PROVIDE_IOAPI_INLINE ( arm, writeq );
#else
PROVIDE_IOAPI ( arm, readq, arm32_readq );
PROVIDE_IOAPI ( arm, writeq, arm32_writeq );
#endif

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_ACPI_H
#define _BITS_ACPI_H
/** @file
*
* ARM-specific ACPI API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ACPI_H */

View File

@@ -0,0 +1,13 @@
#ifndef _BITS_ENDIAN_H
#define _BITS_ENDIAN_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* ARM may be either little-endian or big-endian */
#ifdef __ARM_BIG_ENDIAN
#define __BYTE_ORDER __BIG_ENDIAN
#else
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
#endif /* _BITS_ENDIAN_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_ENTROPY_H
#define _BITS_ENTROPY_H
/** @file
*
* ARM-specific entropy API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_ENTROPY_H */

View File

@@ -0,0 +1,19 @@
#ifndef _BITS_ERRFILE_H
#define _BITS_ERRFILE_H
/** @file
*
* ARM-specific error file identifiers
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @addtogroup errfile Error file identifiers
* @{
*/
/** @} */
#endif /* _BITS_ERRFILE_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_HYPERV_H
#define _BITS_HYPERV_H
/** @file
*
* Hyper-V interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_HYPERV_H */

View File

@@ -0,0 +1,14 @@
#ifndef _BITS_IO_H
#define _BITS_IO_H
/** @file
*
* ARM-specific I/O API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/arm_io.h>
#endif /* _BITS_IO_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_IOMAP_H
#define _BITS_IOMAP_H
/** @file
*
* ARM-specific I/O mapping API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_IOMAP_H */

View File

@@ -0,0 +1,14 @@
#ifndef _BITS_NAP_H
#define _BITS_NAP_H
/** @file
*
* ARM-specific CPU sleeping API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efiarm_nap.h>
#endif /* _BITS_MAP_H */

View File

@@ -0,0 +1,14 @@
#ifndef _BITS_PCI_IO_H
#define _BITS_PCI_IO_H
/** @file
*
* ARM PCI I/O API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/io.h>
#endif /* _BITS_PCI_IO_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_REBOOT_H
#define _BITS_REBOOT_H
/** @file
*
* ARM-specific reboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_REBOOT_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_SANBOOT_H
#define _BITS_SANBOOT_H
/** @file
*
* ARM-specific sanboot API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_SANBOOT_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_SMBIOS_H
#define _BITS_SMBIOS_H
/** @file
*
* ARM-specific SMBIOS API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_SMBIOS_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_TIME_H
#define _BITS_TIME_H
/** @file
*
* ARM-specific time API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_TIME_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_UACCESS_H
#define _BITS_UACCESS_H
/** @file
*
* ARM-specific user access API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_UACCESS_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_UART_H
#define _BITS_UART_H
/** @file
*
* 16550-compatible UART
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_UART_H */

View File

@@ -0,0 +1,12 @@
#ifndef _BITS_UMALLOC_H
#define _BITS_UMALLOC_H
/** @file
*
* ARM-specific user memory allocation API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#endif /* _BITS_UMALLOC_H */

View File

@@ -0,0 +1,158 @@
#ifndef _BITS_XEN_H
#define _BITS_XEN_H
/** @file
*
* Xen interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* Hypercall registers */
#ifdef __aarch64__
#define XEN_HC "x16"
#define XEN_REG1 "x0"
#define XEN_REG2 "x1"
#define XEN_REG3 "x2"
#define XEN_REG4 "x3"
#define XEN_REG5 "x4"
#else
#define XEN_HC "r12"
#define XEN_REG1 "r0"
#define XEN_REG2 "r1"
#define XEN_REG3 "r2"
#define XEN_REG4 "r3"
#define XEN_REG5 "r4"
#endif
/**
* Issue hypercall with one argument
*
* @v xen Xen hypervisor
* @v hypercall Hypercall number
* @v arg1 First argument
* @ret retval Return value
*/
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_1 ( struct xen_hypervisor *xen __unused, unsigned int hypercall,
unsigned long arg1 ) {
register unsigned long hc asm ( XEN_HC ) = hypercall;
register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
__asm__ __volatile__ ( "hvc %1"
: "+r" ( reg1 )
: "i" ( XEN_HYPERCALL_TAG ), "r" ( hc )
: "memory", "cc" );
return reg1;
}
/**
* Issue hypercall with two arguments
*
* @v xen Xen hypervisor
* @v hypercall Hypercall number
* @v arg1 First argument
* @v arg2 Second argument
* @ret retval Return value
*/
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_2 ( struct xen_hypervisor *xen __unused, unsigned int hypercall,
unsigned long arg1, unsigned long arg2 ) {
register unsigned long hc asm ( XEN_HC ) = hypercall;
register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
__asm__ __volatile__ ( "hvc %2"
: "+r" ( reg1 ), "+r" ( reg2 )
: "i" ( XEN_HYPERCALL_TAG ), "r" ( hc )
: "memory", "cc" );
return reg1;
}
/**
* Issue hypercall with three arguments
*
* @v xen Xen hypervisor
* @v hypercall Hypercall number
* @v arg1 First argument
* @v arg2 Second argument
* @v arg3 Third argument
* @ret retval Return value
*/
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_3 ( struct xen_hypervisor *xen __unused, unsigned int hypercall,
unsigned long arg1, unsigned long arg2, unsigned long arg3 ) {
register unsigned long hc asm ( XEN_HC ) = hypercall;
register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
__asm__ __volatile__ ( "hvc %3"
: "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 )
: "i" ( XEN_HYPERCALL_TAG ), "r" ( hc )
: "memory", "cc" );
return reg1;
}
/**
* Issue hypercall with four arguments
*
* @v xen Xen hypervisor
* @v hypercall Hypercall number
* @v arg1 First argument
* @v arg2 Second argument
* @v arg3 Third argument
* @v arg4 Fourth argument
* @ret retval Return value
*/
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_4 ( struct xen_hypervisor *xen __unused, unsigned int hypercall,
unsigned long arg1, unsigned long arg2, unsigned long arg3,
unsigned long arg4 ) {
register unsigned long hc asm ( XEN_HC ) = hypercall;
register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
__asm__ __volatile__ ( "hvc %4"
: "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ),
"+r" ( reg4 )
: "i" ( XEN_HYPERCALL_TAG ), "r" ( hc )
: "memory", "cc" );
return reg1;
}
/**
* Issue hypercall with five arguments
*
* @v xen Xen hypervisor
* @v hypercall Hypercall number
* @v arg1 First argument
* @v arg2 Second argument
* @v arg3 Third argument
* @v arg4 Fourth argument
* @v arg5 Fifth argument
* @ret retval Return value
*/
static inline __attribute__ (( always_inline )) unsigned long
xen_hypercall_5 ( struct xen_hypervisor *xen __unused, unsigned int hypercall,
unsigned long arg1, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5 ) {
register unsigned long hc asm ( XEN_HC ) = hypercall;
register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
register unsigned long reg5 asm ( XEN_REG5 ) = arg5;
__asm__ __volatile__ ( "hvc %5"
: "+r" ( reg1 ), "+r" ( reg2 ), "+r" ( reg3 ),
"+r" ( reg4 ), "+r" ( reg5 )
: "i" ( XEN_HYPERCALL_TAG ), "r" ( hc )
: "memory", "cc" );
return reg1;
}
#endif /* _BITS_XEN_H */

View File

@@ -0,0 +1,146 @@
#ifndef _IPXE_ARM_IO_H
#define _IPXE_ARM_IO_H
/** @file
*
* iPXE I/O API for ARM
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef IOAPI_ARM
#define IOAPI_PREFIX_arm
#else
#define IOAPI_PREFIX_arm __arm_
#endif
/*
* Memory space mappings
*
*/
/** Page shift */
#define PAGE_SHIFT 12
/*
* Physical<->Bus address mappings
*
*/
static inline __always_inline unsigned long
IOAPI_INLINE ( arm, phys_to_bus ) ( unsigned long phys_addr ) {
return phys_addr;
}
static inline __always_inline unsigned long
IOAPI_INLINE ( arm, bus_to_phys ) ( unsigned long bus_addr ) {
return bus_addr;
}
/*
* MMIO reads and writes up to native word size
*
*/
#define ARM_READX( _suffix, _type, _insn_suffix, _reg_prefix ) \
static inline __always_inline _type \
IOAPI_INLINE ( arm, read ## _suffix ) ( volatile _type *io_addr ) { \
_type data; \
__asm__ __volatile__ ( "ldr" _insn_suffix " %" _reg_prefix "0, %1" \
: "=r" ( data ) : "Qo" ( *io_addr ) ); \
return data; \
}
#ifdef __aarch64__
ARM_READX ( b, uint8_t, "b", "w" );
ARM_READX ( w, uint16_t, "h", "w" );
ARM_READX ( l, uint32_t, "", "w" );
ARM_READX ( q, uint64_t, "", "" );
#else
ARM_READX ( b, uint8_t, "b", "" );
ARM_READX ( w, uint16_t, "h", "" );
ARM_READX ( l, uint32_t, "", "" );
#endif
#define ARM_WRITEX( _suffix, _type, _insn_suffix, _reg_prefix ) \
static inline __always_inline void \
IOAPI_INLINE ( arm, write ## _suffix ) ( _type data, \
volatile _type *io_addr ) { \
__asm__ __volatile__ ( "str" _insn_suffix " %" _reg_prefix "0, %1" \
: : "r" ( data ), "Qo" ( *io_addr ) ); \
}
#ifdef __aarch64__
ARM_WRITEX ( b, uint8_t, "b", "w" );
ARM_WRITEX ( w, uint16_t, "h", "w" );
ARM_WRITEX ( l, uint32_t, "", "w" );
ARM_WRITEX ( q, uint64_t, "", "" );
#else
ARM_WRITEX ( b, uint8_t, "b", "" );
ARM_WRITEX ( w, uint16_t, "h", "" );
ARM_WRITEX ( l, uint32_t, "", "" );
#endif
/*
* Dummy PIO reads and writes up to 32 bits
*
* There is no common standard for I/O-space access for ARM, and
* non-MMIO peripherals are vanishingly rare. Provide dummy
* implementations that will allow code to link and should cause
* drivers to simply fail to detect hardware at runtime.
*
*/
#define ARM_INX( _suffix, _type ) \
static inline __always_inline _type \
IOAPI_INLINE ( arm, in ## _suffix ) ( volatile _type *io_addr __unused) { \
return ~( (_type) 0 ); \
} \
static inline __always_inline void \
IOAPI_INLINE ( arm, ins ## _suffix ) ( volatile _type *io_addr __unused, \
_type *data, unsigned int count ) { \
memset ( data, 0xff, count * sizeof ( *data ) ); \
}
ARM_INX ( b, uint8_t );
ARM_INX ( w, uint16_t );
ARM_INX ( l, uint32_t );
#define ARM_OUTX( _suffix, _type ) \
static inline __always_inline void \
IOAPI_INLINE ( arm, out ## _suffix ) ( _type data __unused, \
volatile _type *io_addr __unused ) { \
/* Do nothing */ \
} \
static inline __always_inline void \
IOAPI_INLINE ( arm, outs ## _suffix ) ( volatile _type *io_addr __unused, \
const _type *data __unused, \
unsigned int count __unused ) { \
/* Do nothing */ \
}
ARM_OUTX ( b, uint8_t );
ARM_OUTX ( w, uint16_t );
ARM_OUTX ( l, uint32_t );
/*
* Slow down I/O
*
*/
static inline __always_inline void
IOAPI_INLINE ( arm, iodelay ) ( void ) {
/* Nothing to do */
}
/*
* Memory barrier
*
*/
static inline __always_inline void
IOAPI_INLINE ( arm, mb ) ( void ) {
#ifdef __aarch64__
__asm__ __volatile__ ( "dmb sy" );
#else
__asm__ __volatile__ ( "dmb" );
#endif
}
#endif /* _IPXE_ARM_IO_H */

View File

@@ -0,0 +1,18 @@
#ifndef _IPXE_EFIARM_NAP_H
#define _IPXE_EFIARM_NAP_H
/** @file
*
* EFI CPU sleeping
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef NAP_EFIARM
#define NAP_PREFIX_efiarm
#else
#define NAP_PREFIX_efiarm __efiarm_
#endif
#endif /* _IPXE_EFIARM_NAP_H */

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/nap.h>
#include <ipxe/efi/efi.h>
/** @file
*
* iPXE CPU sleeping API for EFI
*
*/
/**
* Sleep until next interrupt
*
*/
static void efiarm_cpu_nap ( void ) {
/*
* I can't find any EFI API that allows us to put the CPU to
* sleep. The CpuSleep() function is defined in CpuLib.h, but
* isn't part of any exposed protocol so we have no way to
* call it.
*
* The EFI shell doesn't seem to bother sleeping the CPU; it
* just sits there idly burning power.
*
*/
__asm__ __volatile__ ( "wfi" );
}
PROVIDE_NAP ( efiarm, cpu_nap, efiarm_cpu_nap );

23
src/arch/arm32/Makefile Normal file
View File

@@ -0,0 +1,23 @@
# ARM32-specific directories containing source files
#
SRCDIRS += arch/arm32/core
SRCDIRS += arch/arm32/libgcc
# ARM32-specific flags
#
CFLAGS += -mthumb -mcpu=cortex-a15 -mabi=aapcs -mfloat-abi=soft
CFLAGS += -mword-relocations
ASFLAGS += -mthumb -mcpu=cortex-a15
# EFI requires -fshort-wchar, and nothing else currently uses wchar_t
#
CFLAGS += -fshort-wchar
# Include common ARM Makefile
MAKEDEPS += arch/arm/Makefile
include arch/arm/Makefile
# Include platform-specific Makefile
#
MAKEDEPS += arch/arm32/Makefile.$(PLATFORM)
include arch/arm32/Makefile.$(PLATFORM)

View File

@@ -0,0 +1,18 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# UEFI requires that enums are always 32 bits
#
CFLAGS += -fno-short-enums
# Specify EFI image builder
#
ELF2EFI = $(ELF2EFI32)
# Specify EFI boot file
#
EFI_BOOT_FILE = bootarm.efi
# Include generic EFI Makefile
#
MAKEDEPS += arch/arm/Makefile.efi
include arch/arm/Makefile.efi

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <ipxe/bigint.h>
/** @file
*
* Big integer support
*/
/**
* Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
* @v multiplier0 Element 0 of big integer to be multiplied
* @v result0 Element 0 of big integer to hold result
* @v size Number of elements
*/
void bigint_multiply_raw ( const uint32_t *multiplicand0,
const uint32_t *multiplier0,
uint32_t *result0, unsigned int size ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
( ( const void * ) multiplicand0 );
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
( ( const void * ) multiplier0 );
bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
unsigned int i;
unsigned int j;
uint32_t multiplicand_element;
uint32_t multiplier_element;
uint32_t *result_elements;
uint32_t discard_low;
uint32_t discard_high;
uint32_t discard_temp;
/* Zero result */
memset ( result, 0, sizeof ( *result ) );
/* Multiply integers one element at a time */
for ( i = 0 ; i < size ; i++ ) {
multiplicand_element = multiplicand->element[i];
for ( j = 0 ; j < size ; j++ ) {
multiplier_element = multiplier->element[j];
result_elements = &result->element[ i + j ];
/* Perform a single multiply, and add the
* resulting double-element into the result,
* carrying as necessary. The carry can
* never overflow beyond the end of the
* result, since:
*
* a < 2^{n}, b < 2^{n} => ab < 2^{2n}
*/
__asm__ __volatile__ ( "umull %1, %2, %5, %6\n\t"
"ldr %3, [%0]\n\t"
"adds %3, %1\n\t"
"stmia %0!, {%3}\n\t"
"ldr %3, [%0]\n\t"
"adcs %3, %2\n\t"
"stmia %0!, {%3}\n\t"
"bcc 2f\n\t"
"\n1:\n\t"
"ldr %3, [%0]\n\t"
"adcs %3, #0\n\t"
"stmia %0!, {%3}\n\t"
"bcs 1b\n\t"
"\n2:\n\t"
: "+l" ( result_elements ),
"=l" ( discard_low ),
"=l" ( discard_high ),
"=l" ( discard_temp ),
"+m" ( *result )
: "l" ( multiplicand_element ),
"l" ( multiplier_element )
: "cc" );
}
}
}

View File

@@ -0,0 +1,32 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.arm
/*
* Save stack context for non-local goto
*/
.globl setjmp
.type setjmp, %function
setjmp:
/* Store registers */
stmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr }
/* Return 0 when returning as setjmp() */
mov r0, #0
bx lr
.size setjmp, . - setjmp
/*
* Non-local jump to a saved stack context
*/
.globl longjmp
.type longjmp, %function
longjmp:
/* Restore registers */
ldmia r0, { r4, r5, r6, r7, r8, r9, r10, fp, sp, lr }
/* Force result to non-zero */
movs r0, r1
moveq r0, #1
/* Return to setjmp() caller */
bx lr
.size longjmp, . - longjmp

View File

@@ -0,0 +1,316 @@
#ifndef _BITS_BIGINT_H
#define _BITS_BIGINT_H
/** @file
*
* Big integer support
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <strings.h>
/** Element of a big integer */
typedef uint32_t bigint_element_t;
/**
* Initialise big integer
*
* @v value0 Element 0 of big integer to initialise
* @v size Number of elements
* @v data Raw data
* @v len Length of raw data
*/
static inline __attribute__ (( always_inline )) void
bigint_init_raw ( uint32_t *value0, unsigned int size,
const void *data, size_t len ) {
size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len );
uint8_t *value_byte = ( ( void * ) value0 );
const uint8_t *data_byte = ( data + len );
/* Copy raw data in reverse order, padding with zeros */
while ( len-- )
*(value_byte++) = *(--data_byte);
while ( pad_len-- )
*(value_byte++) = 0;
}
/**
* Add big integers
*
* @v addend0 Element 0 of big integer to add
* @v value0 Element 0 of big integer to be added to
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_add_raw ( const uint32_t *addend0, uint32_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint32_t *discard_addend;
uint32_t *discard_value;
uint32_t *discard_end;
uint32_t discard_addend_i;
uint32_t discard_value_i;
__asm__ __volatile__ ( "adds %2, %0, %8, lsl #2\n\t" /* clear CF */
"\n1:\n\t"
"ldmia %0!, {%3}\n\t"
"ldr %4, [%1]\n\t"
"adcs %4, %3\n\t"
"stmia %1!, {%4}\n\t"
"teq %0, %2\n\t"
"bne 1b\n\t"
: "=l" ( discard_addend ),
"=l" ( discard_value ),
"=l" ( discard_end ),
"=l" ( discard_addend_i ),
"=l" ( discard_value_i ),
"+m" ( *value )
: "0" ( addend0 ), "1" ( value0 ), "l" ( size )
: "cc" );
}
/**
* Subtract big integers
*
* @v subtrahend0 Element 0 of big integer to subtract
* @v value0 Element 0 of big integer to be subtracted from
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint32_t *discard_subtrahend;
uint32_t *discard_value;
uint32_t *discard_end;
uint32_t discard_subtrahend_i;
uint32_t discard_value_i;
__asm__ __volatile__ ( "add %2, %0, %8, lsl #2\n\t"
"cmp %2, %0\n\t" /* set CF */
"\n1:\n\t"
"ldmia %0!, {%3}\n\t"
"ldr %4, [%1]\n\t"
"sbcs %4, %3\n\t"
"stmia %1!, {%4}\n\t"
"teq %0, %2\n\t"
"bne 1b\n\t"
: "=l" ( discard_subtrahend ),
"=l" ( discard_value ),
"=l" ( discard_end ),
"=l" ( discard_subtrahend_i ),
"=l" ( discard_value_i ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
"l" ( size )
: "cc" );
}
/**
* Rotate big integer left
*
* @v value0 Element 0 of big integer
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_rol_raw ( uint32_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint32_t *discard_value;
uint32_t *discard_end;
uint32_t discard_value_i;
__asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */
"\n1:\n\t"
"ldr %2, [%0]\n\t"
"adcs %2, %2\n\t"
"stmia %0!, {%2}\n\t"
"teq %0, %1\n\t"
"bne 1b\n\t"
: "=l" ( discard_value ),
"=l" ( discard_end ),
"=l" ( discard_value_i ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size )
: "cc" );
}
/**
* Rotate big integer right
*
* @v value0 Element 0 of big integer
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_ror_raw ( uint32_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint32_t *discard_value;
uint32_t *discard_end;
uint32_t discard_value_i;
__asm__ __volatile__ ( "adds %1, %0, %5, lsl #2\n\t" /* clear CF */
"\n1:\n\t"
"ldmdb %1!, {%2}\n\t"
"rrxs %2, %2\n\t"
"str %2, [%1]\n\t"
"teq %0, %1\n\t"
"bne 1b\n\t"
: "=l" ( discard_value ),
"=l" ( discard_end ),
"=l" ( discard_value_i ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size )
: "cc" );
}
/**
* Test if big integer is equal to zero
*
* @v value0 Element 0 of big integer
* @v size Number of elements
* @ret is_zero Big integer is equal to zero
*/
static inline __attribute__ (( always_inline, pure )) int
bigint_is_zero_raw ( const uint32_t *value0, unsigned int size ) {
const uint32_t *value = value0;
uint32_t value_i;
do {
value_i = *(value++);
if ( value_i )
break;
} while ( --size );
return ( value_i == 0 );
}
/**
* Compare big integers
*
* @v value0 Element 0 of big integer
* @v reference0 Element 0 of reference big integer
* @v size Number of elements
* @ret geq Big integer is greater than or equal to the reference
*/
static inline __attribute__ (( always_inline, pure )) int
bigint_is_geq_raw ( const uint32_t *value0, const uint32_t *reference0,
unsigned int size ) {
const uint32_t *value = ( value0 + size );
const uint32_t *reference = ( reference0 + size );
uint32_t value_i;
uint32_t reference_i;
do {
value_i = *(--value);
reference_i = *(--reference);
if ( value_i != reference_i )
break;
} while ( --size );
return ( value_i >= reference_i );
}
/**
* Test if bit is set in big integer
*
* @v value0 Element 0 of big integer
* @v size Number of elements
* @v bit Bit to test
* @ret is_set Bit is set
*/
static inline __attribute__ (( always_inline )) int
bigint_bit_is_set_raw ( const uint32_t *value0, unsigned int size,
unsigned int bit ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( const void * ) value0 );
unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) );
unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) );
return ( value->element[index] & ( 1 << subindex ) );
}
/**
* Find highest bit set in big integer
*
* @v value0 Element 0 of big integer
* @v size Number of elements
* @ret max_bit Highest bit set + 1 (or 0 if no bits set)
*/
static inline __attribute__ (( always_inline )) int
bigint_max_set_bit_raw ( const uint32_t *value0, unsigned int size ) {
const uint32_t *value = ( value0 + size );
int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) );
uint32_t value_i;
do {
value_i = *(--value);
max_bit -= ( 32 - fls ( value_i ) );
if ( value_i )
break;
} while ( --size );
return max_bit;
}
/**
* Grow big integer
*
* @v source0 Element 0 of source big integer
* @v source_size Number of elements in source big integer
* @v dest0 Element 0 of destination big integer
* @v dest_size Number of elements in destination big integer
*/
static inline __attribute__ (( always_inline )) void
bigint_grow_raw ( const uint32_t *source0, unsigned int source_size,
uint32_t *dest0, unsigned int dest_size ) {
unsigned int pad_size = ( dest_size - source_size );
memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) );
memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) );
}
/**
* Shrink big integer
*
* @v source0 Element 0 of source big integer
* @v source_size Number of elements in source big integer
* @v dest0 Element 0 of destination big integer
* @v dest_size Number of elements in destination big integer
*/
static inline __attribute__ (( always_inline )) void
bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused,
uint32_t *dest0, unsigned int dest_size ) {
memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) );
}
/**
* Finalise big integer
*
* @v value0 Element 0 of big integer to finalise
* @v size Number of elements
* @v out Output buffer
* @v len Length of output buffer
*/
static inline __attribute__ (( always_inline )) void
bigint_done_raw ( const uint32_t *value0, unsigned int size __unused,
void *out, size_t len ) {
const uint8_t *value_byte = ( ( const void * ) value0 );
uint8_t *out_byte = ( out + len );
/* Copy raw data in reverse order */
while ( len-- )
*(--out_byte) = *(value_byte++);
}
extern void bigint_multiply_raw ( const uint32_t *multiplicand0,
const uint32_t *multiplier0,
uint32_t *value0, unsigned int size );
#endif /* _BITS_BIGINT_H */

View File

@@ -0,0 +1,100 @@
#ifndef _BITS_BITOPS_H
#define _BITS_BITOPS_H
/** @file
*
* ARM bit operations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/**
* Test and set bit atomically
*
* @v bit Bit to set
* @v bits Bit field
* @ret old Old value of bit (zero or non-zero)
*/
static inline __attribute__ (( always_inline )) int
test_and_set_bit ( unsigned int bit, volatile void *bits ) {
unsigned int index = ( bit / 32 );
unsigned int offset = ( bit % 32 );
volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index );
uint32_t mask = ( 1UL << offset );
uint32_t old;
uint32_t new;
uint32_t flag;
__asm__ __volatile__ ( "\n1:\n\t"
"ldrex %0, %3\n\t"
"orr %1, %0, %4\n\t"
"strex %2, %1, %3\n\t"
"tst %2, %2\n\t"
"bne 1b\n\t"
: "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ),
"+Q" ( *dword )
: "r" ( mask )
: "cc" );
return ( old & mask );
}
/**
* Test and clear bit atomically
*
* @v bit Bit to set
* @v bits Bit field
* @ret old Old value of bit (zero or non-zero)
*/
static inline __attribute__ (( always_inline )) int
test_and_clear_bit ( unsigned int bit, volatile void *bits ) {
unsigned int index = ( bit / 32 );
unsigned int offset = ( bit % 32 );
volatile uint32_t *dword = ( ( ( volatile uint32_t * ) bits ) + index );
uint32_t mask = ( 1UL << offset );
uint32_t old;
uint32_t new;
uint32_t flag;
__asm__ __volatile__ ( "\n1:\n\t"
"ldrex %0, %3\n\t"
"bic %1, %0, %4\n\t"
"strex %2, %1, %3\n\t"
"tst %2, %2\n\t"
"bne 1b\n\t"
: "=&r" ( old ), "=&r" ( new ), "=&l" ( flag ),
"+Q" ( *dword )
: "r" ( mask )
: "cc" );
return ( old & mask );
}
/**
* Set bit atomically
*
* @v bit Bit to set
* @v bits Bit field
*/
static inline __attribute__ (( always_inline )) void
set_bit ( unsigned int bit, volatile void *bits ) {
test_and_set_bit ( bit, bits );
}
/**
* Clear bit atomically
*
* @v bit Bit to set
* @v bits Bit field
*/
static inline __attribute__ (( always_inline )) void
clear_bit ( unsigned int bit, volatile void *bits ) {
test_and_clear_bit ( bit, bits );
}
#endif /* _BITS_BITOPS_H */

View File

@@ -0,0 +1,52 @@
#ifndef _BITS_BYTESWAP_H
#define _BITS_BYTESWAP_H
/** @file
*
* Byte-order swapping functions
*
*/
#include <stdint.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static inline __attribute__ (( always_inline, const )) uint16_t
__bswap_variable_16 ( uint16_t x ) {
__asm__ ( "rev16 %0, %1" : "=l" ( x ) : "l" ( x ) );
return x;
}
static inline __attribute__ (( always_inline )) void
__bswap_16s ( uint16_t *x ) {
*x = __bswap_variable_16 ( *x );
}
static inline __attribute__ (( always_inline, const )) uint32_t
__bswap_variable_32 ( uint32_t x ) {
__asm__ ( "rev %0, %1" : "=l" ( x ) : "l" ( x ) );
return x;
}
static inline __attribute__ (( always_inline )) void
__bswap_32s ( uint32_t *x ) {
*x = __bswap_variable_32 ( *x );
}
static inline __attribute__ (( always_inline, const )) uint64_t
__bswap_variable_64 ( uint64_t x ) {
uint32_t in_high = ( x >> 32 );
uint32_t in_low = ( x & 0xffffffffUL );
uint32_t out_high = __bswap_variable_32 ( in_low );
uint32_t out_low = __bswap_variable_32 ( in_high );
return ( ( ( ( uint64_t ) out_high ) << 32 ) |
( ( uint64_t ) out_low ) );
}
static inline __attribute__ (( always_inline )) void
__bswap_64s ( uint64_t *x ) {
*x = __bswap_variable_64 ( *x );
}
#endif

View File

@@ -0,0 +1,16 @@
#ifndef _BITS_COMPILER_H
#define _BITS_COMPILER_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Dummy relocation type */
#define RELOC_TYPE_NONE R_ARM_NONE
#ifndef ASSEMBLY
#define __asmcall
#define __libgcc
#endif /* ASSEMBLY */
#endif /*_BITS_COMPILER_H */

View File

@@ -0,0 +1,30 @@
#ifndef _BITS_PROFILE_H
#define _BITS_PROFILE_H
/** @file
*
* Profiling
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/**
* Get profiling timestamp
*
* @ret timestamp Timestamp
*/
static inline __attribute__ (( always_inline )) uint64_t
profile_timestamp ( void ) {
uint32_t cycles;
/* Read cycle counter */
__asm__ __volatile__ ( "mcr p15, 0, %1, c9, c12, 0\n\t"
"mrc p15, 0, %0, c9, c13, 0\n\t"
: "=r" ( cycles ) : "r" ( 1 ) );
return cycles;
}
#endif /* _BITS_PROFILE_H */

View File

@@ -0,0 +1,23 @@
#ifndef _BITS_STDINT_H
#define _BITS_STDINT_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
typedef __SIZE_TYPE__ size_t;
typedef signed long ssize_t;
typedef signed long off_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef unsigned long physaddr_t;
typedef unsigned long intptr_t;
#endif /* _BITS_STDINT_H */

View File

@@ -0,0 +1,60 @@
#ifndef BITS_STRING_H
#define BITS_STRING_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* String functions
*
*/
/**
* Fill memory region
*
* @v dest Destination region
* @v character Fill character
* @v len Length
* @ret dest Destination region
*/
static inline __attribute__ (( always_inline )) void *
memset ( void *dest, int character, size_t len ) {
/* Not yet optimised */
generic_memset ( dest, character, len );
return dest;
}
/**
* Copy memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
* @ret dest Destination region
*/
static inline __attribute__ (( always_inline )) void *
memcpy ( void *dest, const void *src, size_t len ) {
/* Not yet optimised */
generic_memcpy ( dest, src, len );
return dest;
}
/**
* Copy (possibly overlapping) memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
* @ret dest Destination region
*/
static inline __attribute__ (( always_inline )) void *
memmove ( void *dest, const void *src, size_t len ) {
/* Not yet optimised */
generic_memmove ( dest, src, len );
return dest;
}
#endif /* BITS_STRING_H */

View File

@@ -0,0 +1,85 @@
#ifndef _BITS_STRINGS_H
#define _BITS_STRINGS_H
/** @file
*
* String functions
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Find first (i.e. least significant) set bit
*
* @v value Value
* @ret lsb Least significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __ffsl ( long value ) {
unsigned long bits = value;
unsigned long lsb;
unsigned int lz;
/* Extract least significant set bit */
lsb = ( bits & -bits );
/* Count number of leading zeroes before LSB */
__asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) );
return ( 32 - lz );
}
/**
* Find first (i.e. least significant) set bit
*
* @v value Value
* @ret lsb Least significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){
unsigned long high = ( value >> 32 );
unsigned long low = ( value >> 0 );
if ( low ) {
return ( __ffsl ( low ) );
} else if ( high ) {
return ( 32 + __ffsl ( high ) );
} else {
return 0;
}
}
/**
* Find last (i.e. most significant) set bit
*
* @v value Value
* @ret msb Most significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __flsl ( long value ) {
unsigned int lz;
/* Count number of leading zeroes */
__asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) );
return ( 32 - lz );
}
/**
* Find last (i.e. most significant) set bit
*
* @v value Value
* @ret msb Most significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __flsll ( long long value ){
unsigned long high = ( value >> 32 );
unsigned long low = ( value >> 0 );
if ( high ) {
return ( 32 + __flsl ( high ) );
} else if ( low ) {
return ( __flsl ( low ) );
} else {
return 0;
}
}
#endif /* _BITS_STRINGS_H */

View File

@@ -0,0 +1,19 @@
#ifndef _BITS_TCPIP_H
#define _BITS_TCPIP_H
/** @file
*
* Transport-network layer interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static inline __attribute__ (( always_inline )) uint16_t
tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ) {
/* Not yet optimised */
return generic_tcpip_continue_chksum ( partial, data, len );
}
#endif /* _BITS_TCPIP_H */

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
#ifndef _DHCP_ARCH_H
#define _DHCP_ARCH_H
/** @file
*
* Architecture-specific DHCP options
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/dhcp.h>
#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM32
#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
#endif

View File

@@ -0,0 +1,45 @@
#ifndef GDBMACH_H
#define GDBMACH_H
/** @file
*
* GDB architecture specifics
*
* This file declares functions for manipulating the machine state and
* debugging context.
*
*/
#include <stdint.h>
typedef unsigned long gdbreg_t;
/* Register snapshot */
enum {
/* Not yet implemented */
GDBMACH_NREGS,
};
#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) )
static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
/* Not yet implemented */
( void ) regs;
( void ) pc;
}
static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) {
/* Not yet implemented */
( void ) regs;
( void ) step;
}
static inline void gdbmach_breakpoint ( void ) {
/* Not yet implemented */
}
extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
int enable );
extern void gdbmach_init ( void );
#endif /* GDBMACH_H */

View File

@@ -0,0 +1,61 @@
#ifndef LIMITS_H
#define LIMITS_H 1
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* Number of bits in a `char' */
#define CHAR_BIT 8
/* Minimum and maximum values a `signed char' can hold */
#define SCHAR_MIN (-128)
#define SCHAR_MAX 127
/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
#define UCHAR_MAX 255
/* Minimum and maximum values a `char' can hold */
#define CHAR_MIN SCHAR_MIN
#define CHAR_MAX SCHAR_MAX
/* Minimum and maximum values a `signed short int' can hold */
#define SHRT_MIN (-32768)
#define SHRT_MAX 32767
/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
#define USHRT_MAX 65535
/* Minimum and maximum values a `signed int' can hold */
#define INT_MIN (-INT_MAX - 1)
#define INT_MAX 2147483647
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed int' can hold */
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed long' can hold */
#define LONG_MAX 2147483647
#define LONG_MIN (-LONG_MAX - 1L)
/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
#define ULONG_MAX 4294967295UL
/* Minimum and maximum values a `signed long long' can hold */
#define LLONG_MAX 9223372036854775807LL
#define LLONG_MIN (-LONG_MAX - 1LL)
/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
#define ULLONG_MAX 18446744073709551615ULL
#endif /* LIMITS_H */

View File

@@ -0,0 +1,38 @@
#ifndef _SETJMP_H
#define _SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/** A jump buffer */
typedef struct {
/** Saved r4 */
uint32_t r4;
/** Saved r5 */
uint32_t r5;
/** Saved r6 */
uint32_t r6;
/** Saved r7 */
uint32_t r7;
/** Saved r8 */
uint32_t r8;
/** Saved r9 */
uint32_t r9;
/** Saved r10 */
uint32_t r10;
/** Saved frame pointer (r11) */
uint32_t fp;
/** Saved stack pointer (r13) */
uint32_t sp;
/** Saved link register (r14) */
uint32_t lr;
} jmp_buf[1];
extern int __asmcall __attribute__ (( returns_twice ))
setjmp ( jmp_buf env );
extern void __asmcall __attribute__ (( noreturn ))
longjmp ( jmp_buf env, int val );
#endif /* _SETJMP_H */

View File

@@ -0,0 +1,50 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.thumb
/**
* Unsigned long long division
*
* @v r1:r0 Dividend
* @v r3:r2 Divisor
* @ret r1:r0 Quotient
* @ret r3:r2 Remainder
*/
.section ".text.__aeabi_uldivmod", "ax", %progbits
.globl __aeabi_uldivmod
.type __aeabi_uldivmod, %function
__aeabi_uldivmod:
/* Allocate stack space for remainder and pointer to remainder */
push {r0, r1, r2, r3, r4, lr}
/* Call __udivmoddi4() */
add r4, sp, #8
str r4, [sp]
bl __udivmoddi4
/* Retrieve remainder and return */
add sp, sp, #8
pop {r2, r3, r4, pc}
.size __aeabi_uldivmod, . - __aeabi_uldivmod
/**
* Signed long long division
*
* @v r1:r0 Dividend
* @v r3:r2 Divisor
* @ret r1:r0 Quotient
* @ret r3:r2 Remainder
*/
.section ".text.__aeabi_ldivmod", "ax", %progbits
.globl __aeabi_ldivmod
.type __aeabi_ldivmod, %function
__aeabi_ldivmod:
/* Allocate stack space for remainder and pointer to remainder */
push {r0, r1, r2, r3, r4, lr}
/* Call __divmoddi4() */
add r4, sp, #8
str r4, [sp]
bl __divmoddi4
/* Retrieve remainder and return */
add sp, sp, #8
pop {r2, r3, r4, pc}
.size __aeabi_ldivmod, . - __aeabi_ldivmod

View File

@@ -0,0 +1,88 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.arm
/**
* Logical shift left
*
* @v r1:r0 Value to shift
* @v r2 Shift amount
* @ret r1:r0 Shifted value
*/
.section ".text.__aeabi_llsl", "ax", %progbits
.globl __aeabi_llsl
.type __aeabi_llsl, %function
__aeabi_llsl:
/* r3 = ( shift - 32 ) */
subs r3, r2, #32
/* If shift >= 32, then
* high = ( low << ( shift - 32 ) )
*/
movpl r1, r0, lsl r3
/* If shift < 32, then
* high = ( ( high << shift ) | ( low >> ( 32 - shift ) ) )
*/
movmi r1, r1, lsl r2
rsbmi r3, r2, #32
orrmi r1, r1, r0, lsr r3
/* low = ( low << shift ) */
mov r0, r0, lsl r2
bx lr
.size __aeabi_llsl, . - __aeabi_llsl
/**
* Logical shift right
*
* @v r1:r0 Value to shift
* @v r2 Shift amount
* @ret r1:r0 Shifted value
*/
.section ".text.__aeabi_llsr", "ax", %progbits
.globl __aeabi_llsr
.type __aeabi_llsr, %function
__aeabi_llsr:
/* r3 = ( shift - 32 ) */
subs r3, r2, #32
/* If shift >= 32, then
* low = ( high >> ( shift - 32 ) )
*/
movpl r0, r1, lsr r3
/* If shift < 32, then
* low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) )
*/
movmi r0, r0, lsr r2
rsbmi r3, r2, #32
orrmi r0, r0, r1, lsl r3
/* high = ( high >> shift ) */
mov r1, r1, lsr r2
bx lr
.size __aeabi_llsr, . - __aeabi_llsr
/**
* Arithmetic shift right
*
* @v r1:r0 Value to shift
* @v r2 Shift amount
* @ret r1:r0 Shifted value
*/
.section ".text.__aeabi_lasr", "ax", %progbits
.globl __aeabi_lasr
.type __aeabi_lasr, %function
__aeabi_lasr:
/* r3 = ( shift - 32 ) */
subs r3, r2, #32
/* If shift >= 32, then
* low = ( high >> ( shift - 32 ) )
*/
movpl r0, r1, asr r3
/* If shift < 32, then
* low = ( ( low >> shift ) | ( high << ( 32 - shift ) ) )
*/
movmi r0, r0, lsr r2
rsbmi r3, r2, #32
orrmi r0, r0, r1, lsl r3
/* high = ( high >> shift ) */
mov r1, r1, asr r2
bx lr
.size __aeabi_lasr, . - __aeabi_lasr

33
src/arch/arm64/Makefile Normal file
View File

@@ -0,0 +1,33 @@
# ARM64-specific directories containing source files
#
SRCDIRS += arch/arm64/core
# ARM64-specific flags
#
CFLAGS += -mlittle-endian -mcmodel=small
CFLAGS += -fomit-frame-pointer
ASFLAGS += -mabi=lp64 -EL
# We want to specify the LP64 model. There is an explicit -mabi=lp64
# on GCC 4.9 and later, and no guarantee as to which is the default
# model. In earlier versions of GCC, there is no -mabi option and the
# default appears to be LP64 anyway.
#
ifeq ($(CCTYPE),gcc)
LP64_TEST = $(CC) -mabi=lp64 -x c -c /dev/null -o /dev/null >/dev/null 2>&1
LP64_FLAGS := $(shell $(LP64_TEST) && $(ECHO) '-mabi=lp64')
WORKAROUND_CFLAGS += $(LP64_FLAGS)
endif
# EFI requires -fshort-wchar, and nothing else currently uses wchar_t
#
CFLAGS += -fshort-wchar
# Include common ARM Makefile
MAKEDEPS += arch/arm/Makefile
include arch/arm/Makefile
# Include platform-specific Makefile
#
MAKEDEPS += arch/arm64/Makefile.$(PLATFORM)
include arch/arm64/Makefile.$(PLATFORM)

View File

@@ -0,0 +1,18 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# Avoid untranslatable relocations
#
CFLAGS += -fno-pic
# Specify EFI image builder
#
ELF2EFI = $(ELF2EFI64)
# Specify EFI boot file
#
EFI_BOOT_FILE = bootaa64.efi
# Include generic EFI Makefile
#
MAKEDEPS += arch/arm/Makefile.efi
include arch/arm/Makefile.efi

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <ipxe/bigint.h>
/** @file
*
* Big integer support
*/
/**
* Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
* @v multiplier0 Element 0 of big integer to be multiplied
* @v result0 Element 0 of big integer to hold result
* @v size Number of elements
*/
void bigint_multiply_raw ( const uint64_t *multiplicand0,
const uint64_t *multiplier0,
uint64_t *result0, unsigned int size ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
( ( const void * ) multiplicand0 );
const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
( ( const void * ) multiplier0 );
bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
unsigned int i;
unsigned int j;
uint64_t multiplicand_element;
uint64_t multiplier_element;
uint64_t *result_elements;
uint64_t discard_low;
uint64_t discard_high;
uint64_t discard_temp_low;
uint64_t discard_temp_high;
/* Zero result */
memset ( result, 0, sizeof ( *result ) );
/* Multiply integers one element at a time */
for ( i = 0 ; i < size ; i++ ) {
multiplicand_element = multiplicand->element[i];
for ( j = 0 ; j < size ; j++ ) {
multiplier_element = multiplier->element[j];
result_elements = &result->element[ i + j ];
/* Perform a single multiply, and add the
* resulting double-element into the result,
* carrying as necessary. The carry can
* never overflow beyond the end of the
* result, since:
*
* a < 2^{n}, b < 2^{n} => ab < 2^{2n}
*/
__asm__ __volatile__ ( "mul %1, %6, %7\n\t"
"umulh %2, %6, %7\n\t"
"ldp %3, %4, [%0]\n\t"
"adds %3, %3, %1\n\t"
"adcs %4, %4, %2\n\t"
"stp %3, %4, [%0], #16\n\t"
"bcc 2f\n\t"
"\n1:\n\t"
"ldr %3, [%0]\n\t"
"adcs %3, %3, xzr\n\t"
"str %3, [%0], #8\n\t"
"bcs 1b\n\t"
"\n2:\n\t"
: "+r" ( result_elements ),
"=&r" ( discard_low ),
"=&r" ( discard_high ),
"=r" ( discard_temp_low ),
"=r" ( discard_temp_high ),
"+m" ( *result )
: "r" ( multiplicand_element ),
"r" ( multiplier_element )
: "cc" );
}
}
}

View File

@@ -0,0 +1,249 @@
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
/** @file
*
* Optimised string operations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
/**
* Copy memory area
*
* @v dest Destination address
* @v src Source address
* @v len Length
* @ret dest Destination address
*/
void arm64_memcpy ( void *dest, const void *src, size_t len ) {
void *discard_dest;
void *discard_end;
const void *discard_src;
size_t discard_offset;
unsigned long discard_data;
unsigned long discard_low;
unsigned long discard_high;
/* If length is too short for an "ldp"/"stp" instruction pair,
* then just copy individual bytes.
*/
if ( len < 16 ) {
__asm__ __volatile__ ( "cbz %0, 2f\n\t"
"\n1:\n\t"
"sub %0, %0, #1\n\t"
"ldrb %w1, [%3, %0]\n\t"
"strb %w1, [%2, %0]\n\t"
"cbnz %0, 1b\n\t"
"\n2:\n\t"
: "=&r" ( discard_offset ),
"=&r" ( discard_data )
: "r" ( dest ), "r" ( src ), "0" ( len )
: "memory" );
return;
}
/* Use "ldp"/"stp" to copy 16 bytes at a time: one initial
* potentially unaligned access, multiple destination-aligned
* accesses, one final potentially unaligned access.
*/
__asm__ __volatile__ ( "ldp %3, %4, [%1], #16\n\t"
"stp %3, %4, [%0], #16\n\t"
"and %3, %0, #15\n\t"
"sub %0, %0, %3\n\t"
"sub %1, %1, %3\n\t"
"bic %2, %5, #15\n\t"
"b 2f\n\t"
"\n1:\n\t"
"ldp %3, %4, [%1], #16\n\t"
"stp %3, %4, [%0], #16\n\t"
"\n2:\n\t"
"cmp %0, %2\n\t"
"bne 1b\n\t"
"ldp %3, %4, [%6, #-16]\n\t"
"stp %3, %4, [%5, #-16]\n\t"
: "=&r" ( discard_dest ),
"=&r" ( discard_src ),
"=&r" ( discard_end ),
"=&r" ( discard_low ),
"=&r" ( discard_high )
: "r" ( dest + len ), "r" ( src + len ),
"0" ( dest ), "1" ( src )
: "memory", "cc" );
}
/**
* Zero memory region
*
* @v dest Destination region
* @v len Length
*/
void arm64_bzero ( void *dest, size_t len ) {
size_t discard_offset;
void *discard_dest;
void *discard_end;
/* If length is too short for an "stp" instruction, then just
* zero individual bytes.
*/
if ( len < 16 ) {
__asm__ __volatile__ ( "cbz %0, 2f\n\t"
"\n1:\n\t"
"sub %0, %0, #1\n\t"
"strb wzr, [%1, %0]\n\t"
"cbnz %0, 1b\n\t"
"\n2:\n\t"
: "=&r" ( discard_offset )
: "r" ( dest ), "0" ( len )
: "memory" );
return;
}
/* Use "stp" to zero 16 bytes at a time: one initial
* potentially unaligned access, multiple aligned accesses,
* one final potentially unaligned access.
*/
__asm__ __volatile__ ( "stp xzr, xzr, [%0], #16\n\t"
"bic %0, %0, #15\n\t"
"bic %1, %2, #15\n\t"
"b 2f\n\t"
"\n1:\n\t"
"stp xzr, xzr, [%0], #16\n\t"
"\n2:\n\t"
"cmp %0, %1\n\t"
"bne 1b\n\t"
"stp xzr, xzr, [%2, #-16]\n\t"
: "=&r" ( discard_dest ),
"=&r" ( discard_end )
: "r" ( dest + len ), "0" ( dest )
: "memory", "cc" );
}
/**
* Fill memory region
*
* @v dest Destination region
* @v len Length
* @v character Fill character
*
* The unusual parameter order is to allow for more efficient
* tail-calling to arm64_memset() when zeroing a region.
*/
void arm64_memset ( void *dest, size_t len, int character ) {
size_t discard_offset;
/* Use optimised zeroing code if applicable */
if ( character == 0 ) {
arm64_bzero ( dest, len );
return;
}
/* Fill one byte at a time. Calling memset() with a non-zero
* value is relatively rare and unlikely to be
* performance-critical.
*/
__asm__ __volatile__ ( "cbz %0, 2f\n\t"
"\n1:\n\t"
"sub %0, %0, #1\n\t"
"strb %w2, [%1, %0]\n\t"
"cbnz %0, 1b\n\t"
"\n2:\n\t"
: "=&r" ( discard_offset )
: "r" ( dest ), "r" ( character ), "0" ( len )
: "memory" );
}
/**
* Copy (possibly overlapping) memory region forwards
*
* @v dest Destination region
* @v src Source region
* @v len Length
*/
void arm64_memmove_forwards ( void *dest, const void *src, size_t len ) {
void *discard_dest;
const void *discard_src;
unsigned long discard_data;
/* Assume memmove() is not performance-critical, and perform a
* bytewise copy for simplicity.
*/
__asm__ __volatile__ ( "b 2f\n\t"
"\n1:\n\t"
"ldrb %w2, [%1], #1\n\t"
"strb %w2, [%0], #1\n\t"
"\n2:\n\t"
"cmp %0, %3\n\t"
"bne 1b\n\t"
: "=&r" ( discard_dest ),
"=&r" ( discard_src ),
"=&r" ( discard_data )
: "r" ( dest + len ), "0" ( dest ), "1" ( src )
: "memory" );
}
/**
* Copy (possibly overlapping) memory region backwards
*
* @v dest Destination region
* @v src Source region
* @v len Length
*/
void arm64_memmove_backwards ( void *dest, const void *src, size_t len ) {
size_t discard_offset;
unsigned long discard_data;
/* Assume memmove() is not performance-critical, and perform a
* bytewise copy for simplicity.
*/
__asm__ __volatile__ ( "cbz %0, 2f\n\t"
"\n1:\n\t"
"sub %0, %0, #1\n\t"
"ldrb %w1, [%3, %0]\n\t"
"strb %w1, [%2, %0]\n\t"
"cbnz %0, 1b\n\t"
"\n2:\n\t"
: "=&r" ( discard_offset ),
"=&r" ( discard_data )
: "r" ( dest ), "r" ( src ), "0" ( len )
: "memory" );
}
/**
* Copy (possibly overlapping) memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
*/
void arm64_memmove ( void *dest, const void *src, size_t len ) {
if ( dest <= src ) {
arm64_memmove_forwards ( dest, src, len );
} else {
arm64_memmove_backwards ( dest, src, len );
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* TCP/IP checksum
*
*/
#include <strings.h>
#include <ipxe/tcpip.h>
/** Alignment used by main checksumming loop */
#define TCPIP_CHKSUM_ALIGN 16
/** Number of steps in each iteration of the unrolled main checksumming loop */
#define TCPIP_CHKSUM_UNROLL 4
/**
* Calculate continued TCP/IP checkum
*
* @v sum Checksum of already-summed data, in network byte order
* @v data Data buffer
* @v len Length of data buffer
* @ret sum Updated checksum, in network byte order
*/
uint16_t tcpip_continue_chksum ( uint16_t sum, const void *data,
size_t len ) {
intptr_t start;
intptr_t end;
intptr_t mid;
unsigned int pre;
unsigned int post;
unsigned int first;
uint64_t discard_low;
uint64_t discard_high;
/* Avoid potentially undefined shift operation */
if ( len == 0 )
return sum;
/* Find maximally-aligned midpoint. For short blocks of data,
* this may be aligned to fewer than 16 bytes.
*/
start = ( ( intptr_t ) data );
end = ( start + len );
mid = ( end &
~( ( ~( 1UL << 63 ) ) >> ( 64 - flsl ( start ^ end ) ) ) );
/* Calculate pre- and post-alignment lengths */
pre = ( ( mid - start ) & ( TCPIP_CHKSUM_ALIGN - 1 ) );
post = ( ( end - mid ) & ( TCPIP_CHKSUM_ALIGN - 1 ) );
/* Calculate number of steps in first iteration of unrolled loop */
first = ( ( ( len - pre - post ) / TCPIP_CHKSUM_ALIGN ) &
( TCPIP_CHKSUM_UNROLL - 1 ) );
/* Calculate checksum */
__asm__ ( /* Invert sum */
"eor %w0, %w0, #0xffff\n\t"
/* Clear carry flag */
"cmn xzr, xzr\n\t"
/* Byteswap and sum pre-alignment byte, if applicable */
"tbz %w4, #0, 1f\n\t"
"ldrb %w2, [%1], #1\n\t"
"rev16 %w0, %w0\n\t"
"rev16 %w2, %w2\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Sum pre-alignment halfword, if applicable */
"tbz %w4, #1, 1f\n\t"
"ldrh %w2, [%1], #2\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Sum pre-alignment word, if applicable */
"tbz %w4, #2, 1f\n\t"
"ldr %w2, [%1], #4\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Sum pre-alignment doubleword, if applicable */
"tbz %w4, #3, 1f\n\t"
"ldr %2, [%1], #8\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Jump into unrolled (x4) main loop */
"adr %2, 2f\n\t"
"sub %2, %2, %5, lsl #3\n\t"
"sub %2, %2, %5, lsl #2\n\t"
"br %2\n\t"
"\n1:\n\t"
"ldp %2, %3, [%1], #16\n\t"
"adcs %0, %0, %2\n\t"
"adcs %0, %0, %3\n\t"
"ldp %2, %3, [%1], #16\n\t"
"adcs %0, %0, %2\n\t"
"adcs %0, %0, %3\n\t"
"ldp %2, %3, [%1], #16\n\t"
"adcs %0, %0, %2\n\t"
"adcs %0, %0, %3\n\t"
"ldp %2, %3, [%1], #16\n\t"
"adcs %0, %0, %2\n\t"
"adcs %0, %0, %3\n\t"
"\n2:\n\t"
"sub %2, %1, %6\n\t"
"cbnz %2, 1b\n\t"
/* Sum post-alignment doubleword, if applicable */
"tbz %w7, #3, 1f\n\t"
"ldr %2, [%1], #8\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Sum post-alignment word, if applicable */
"tbz %w7, #2, 1f\n\t"
"ldr %w2, [%1], #4\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Sum post-alignment halfword, if applicable */
"tbz %w7, #1, 1f\n\t"
"ldrh %w2, [%1], #2\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Sum post-alignment byte, if applicable */
"tbz %w7, #0, 1f\n\t"
"ldrb %w2, [%1], #1\n\t"
"adcs %0, %0, %2\n\t"
"\n1:\n\t"
/* Fold down to a uint32_t plus carry flag */
"lsr %2, %0, #32\n\t"
"adcs %w0, %w0, %w2\n\t"
/* Fold down to a uint16_t plus carry in bit 16 */
"ubfm %2, %0, #0, #15\n\t"
"ubfm %3, %0, #16, #31\n\t"
"adc %w0, %w2, %w3\n\t"
/* Fold down to a uint16_t */
"tbz %w0, #16, 1f\n\t"
"mov %w2, #0xffff\n\t"
"sub %w0, %w0, %w2\n\t"
"tbz %w0, #16, 1f\n\t"
"sub %w0, %w0, %w2\n\t"
"\n1:\n\t"
/* Byteswap back, if applicable */
"tbz %w4, #0, 1f\n\t"
"rev16 %w0, %w0\n\t"
"\n1:\n\t"
/* Invert sum */
"eor %w0, %w0, #0xffff\n\t"
: "+r" ( sum ), "+r" ( data ), "=&r" ( discard_low ),
"=&r" ( discard_high )
: "r" ( pre ), "r" ( first ), "r" ( end - post ),
"r" ( post )
: "cc" );
return sum;
}

View File

@@ -0,0 +1,56 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
/* Must match jmp_buf structure layout */
.struct 0
env_x19_x20: .quad 0, 0
env_x21_x22: .quad 0, 0
env_x23_x24: .quad 0, 0
env_x25_x26: .quad 0, 0
env_x27_x28: .quad 0, 0
env_x29_x30: .quad 0, 0
env_sp: .quad 0
.previous
/*
* Save stack context for non-local goto
*/
.globl setjmp
.type setjmp, %function
setjmp:
/* Store registers */
stp x19, x20, [x0, #env_x19_x20]
stp x21, x22, [x0, #env_x21_x22]
stp x23, x24, [x0, #env_x23_x24]
stp x25, x26, [x0, #env_x25_x26]
stp x27, x28, [x0, #env_x27_x28]
stp x29, x30, [x0, #env_x29_x30]
mov x16, sp
str x16, [x0, #env_sp]
/* Return 0 when returning as setjmp() */
mov x0, #0
ret
.size setjmp, . - setjmp
/*
* Non-local jump to a saved stack context
*/
.globl longjmp
.type longjmp, %function
longjmp:
/* Restore registers */
ldp x19, x20, [x0, #env_x19_x20]
ldp x21, x22, [x0, #env_x21_x22]
ldp x23, x24, [x0, #env_x23_x24]
ldp x25, x26, [x0, #env_x25_x26]
ldp x27, x28, [x0, #env_x27_x28]
ldp x29, x30, [x0, #env_x29_x30]
ldr x16, [x0, #env_sp]
mov sp, x16
/* Force result to non-zero */
cmp w1, #0
csinc w0, w1, w1, ne
/* Return to setjmp() caller */
br x30
.size longjmp, . - longjmp

View File

@@ -0,0 +1,317 @@
#ifndef _BITS_BIGINT_H
#define _BITS_BIGINT_H
/** @file
*
* Big integer support
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <string.h>
#include <strings.h>
/** Element of a big integer */
typedef uint64_t bigint_element_t;
/**
* Initialise big integer
*
* @v value0 Element 0 of big integer to initialise
* @v size Number of elements
* @v data Raw data
* @v len Length of raw data
*/
static inline __attribute__ (( always_inline )) void
bigint_init_raw ( uint64_t *value0, unsigned int size,
const void *data, size_t len ) {
size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len );
uint8_t *value_byte = ( ( void * ) value0 );
const uint8_t *data_byte = ( data + len );
/* Copy raw data in reverse order, padding with zeros */
while ( len-- )
*(value_byte++) = *(--data_byte);
while ( pad_len-- )
*(value_byte++) = 0;
}
/**
* Add big integers
*
* @v addend0 Element 0 of big integer to add
* @v value0 Element 0 of big integer to be added to
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_add_raw ( const uint64_t *addend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_addend;
uint64_t *discard_value;
uint64_t discard_addend_i;
uint64_t discard_value_i;
unsigned int discard_size;
__asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */
"\n1:\n\t"
"ldr %3, [%0], #8\n\t"
"ldr %4, [%1]\n\t"
"adcs %4, %4, %3\n\t"
"str %4, [%1], #8\n\t"
"sub %w2, %w2, #1\n\t"
"cbnz %w2, 1b\n\t"
: "=r" ( discard_addend ),
"=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_addend_i ),
"=r" ( discard_value_i ),
"+m" ( *value )
: "0" ( addend0 ), "1" ( value0 ), "2" ( size )
: "cc" );
}
/**
* Subtract big integers
*
* @v subtrahend0 Element 0 of big integer to subtract
* @v value0 Element 0 of big integer to be subtracted from
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_subtract_raw ( const uint64_t *subtrahend0, uint64_t *value0,
unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_subtrahend;
uint64_t *discard_value;
uint64_t discard_subtrahend_i;
uint64_t discard_value_i;
unsigned int discard_size;
__asm__ __volatile__ ( "cmp xzr, xzr\n\t" /* set CF */
"\n1:\n\t"
"ldr %3, [%0], #8\n\t"
"ldr %4, [%1]\n\t"
"sbcs %4, %4, %3\n\t"
"str %4, [%1], #8\n\t"
"sub %w2, %w2, #1\n\t"
"cbnz %w2, 1b\n\t"
: "=r" ( discard_subtrahend ),
"=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_subtrahend_i ),
"=r" ( discard_value_i ),
"+m" ( *value )
: "0" ( subtrahend0 ), "1" ( value0 ),
"2" ( size )
: "cc" );
}
/**
* Rotate big integer left
*
* @v value0 Element 0 of big integer
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_rol_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
unsigned int discard_size;
__asm__ __volatile__ ( "cmn xzr, xzr\n\t" /* clear CF */
"\n1:\n\t"
"ldr %2, [%0]\n\t"
"adcs %2, %2, %2\n\t"
"str %2, [%0], #8\n\t"
"sub %w1, %w1, #1\n\t"
"cbnz %w1, 1b\n\t"
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size )
: "cc" );
}
/**
* Rotate big integer right
*
* @v value0 Element 0 of big integer
* @v size Number of elements
*/
static inline __attribute__ (( always_inline )) void
bigint_ror_raw ( uint64_t *value0, unsigned int size ) {
bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( void * ) value0 );
uint64_t *discard_value;
uint64_t discard_value_i;
uint64_t discard_value_j;
unsigned int discard_size;
__asm__ __volatile__ ( "mov %3, #0\n\t"
"\n1:\n\t"
"sub %w1, %w1, #1\n\t"
"ldr %2, [%0, %1, lsl #3]\n\t"
"extr %3, %3, %2, #1\n\t"
"str %3, [%0, %1, lsl #3]\n\t"
"mov %3, %2\n\t"
"cbnz %w1, 1b\n\t"
: "=r" ( discard_value ),
"=r" ( discard_size ),
"=r" ( discard_value_i ),
"=r" ( discard_value_j ),
"+m" ( *value )
: "0" ( value0 ), "1" ( size ) );
}
/**
* Test if big integer is equal to zero
*
* @v value0 Element 0 of big integer
* @v size Number of elements
* @ret is_zero Big integer is equal to zero
*/
static inline __attribute__ (( always_inline, pure )) int
bigint_is_zero_raw ( const uint64_t *value0, unsigned int size ) {
const uint64_t *value = value0;
uint64_t value_i;
do {
value_i = *(value++);
if ( value_i )
break;
} while ( --size );
return ( value_i == 0 );
}
/**
* Compare big integers
*
* @v value0 Element 0 of big integer
* @v reference0 Element 0 of reference big integer
* @v size Number of elements
* @ret geq Big integer is greater than or equal to the reference
*/
static inline __attribute__ (( always_inline, pure )) int
bigint_is_geq_raw ( const uint64_t *value0, const uint64_t *reference0,
unsigned int size ) {
const uint64_t *value = ( value0 + size );
const uint64_t *reference = ( reference0 + size );
uint64_t value_i;
uint64_t reference_i;
do {
value_i = *(--value);
reference_i = *(--reference);
if ( value_i != reference_i )
break;
} while ( --size );
return ( value_i >= reference_i );
}
/**
* Test if bit is set in big integer
*
* @v value0 Element 0 of big integer
* @v size Number of elements
* @v bit Bit to test
* @ret is_set Bit is set
*/
static inline __attribute__ (( always_inline )) int
bigint_bit_is_set_raw ( const uint64_t *value0, unsigned int size,
unsigned int bit ) {
const bigint_t ( size ) __attribute__ (( may_alias )) *value =
( ( const void * ) value0 );
unsigned int index = ( bit / ( 8 * sizeof ( value->element[0] ) ) );
unsigned int subindex = ( bit % ( 8 * sizeof ( value->element[0] ) ) );
return ( !! ( value->element[index] & ( 1UL << subindex ) ) );
}
/**
* Find highest bit set in big integer
*
* @v value0 Element 0 of big integer
* @v size Number of elements
* @ret max_bit Highest bit set + 1 (or 0 if no bits set)
*/
static inline __attribute__ (( always_inline )) int
bigint_max_set_bit_raw ( const uint64_t *value0, unsigned int size ) {
const uint64_t *value = ( value0 + size );
int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) );
uint64_t value_i;
do {
value_i = *(--value);
max_bit -= ( 64 - fls ( value_i ) );
if ( value_i )
break;
} while ( --size );
return max_bit;
}
/**
* Grow big integer
*
* @v source0 Element 0 of source big integer
* @v source_size Number of elements in source big integer
* @v dest0 Element 0 of destination big integer
* @v dest_size Number of elements in destination big integer
*/
static inline __attribute__ (( always_inline )) void
bigint_grow_raw ( const uint64_t *source0, unsigned int source_size,
uint64_t *dest0, unsigned int dest_size ) {
unsigned int pad_size = ( dest_size - source_size );
memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) );
memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) );
}
/**
* Shrink big integer
*
* @v source0 Element 0 of source big integer
* @v source_size Number of elements in source big integer
* @v dest0 Element 0 of destination big integer
* @v dest_size Number of elements in destination big integer
*/
static inline __attribute__ (( always_inline )) void
bigint_shrink_raw ( const uint64_t *source0, unsigned int source_size __unused,
uint64_t *dest0, unsigned int dest_size ) {
memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) );
}
/**
* Finalise big integer
*
* @v value0 Element 0 of big integer to finalise
* @v size Number of elements
* @v out Output buffer
* @v len Length of output buffer
*/
static inline __attribute__ (( always_inline )) void
bigint_done_raw ( const uint64_t *value0, unsigned int size __unused,
void *out, size_t len ) {
const uint8_t *value_byte = ( ( const void * ) value0 );
uint8_t *out_byte = ( out + len );
/* Copy raw data in reverse order */
while ( len-- )
*(--out_byte) = *(value_byte++);
}
extern void bigint_multiply_raw ( const uint64_t *multiplicand0,
const uint64_t *multiplier0,
uint64_t *value0, unsigned int size );
#endif /* _BITS_BIGINT_H */

View File

@@ -0,0 +1,100 @@
#ifndef _BITS_BITOPS_H
#define _BITS_BITOPS_H
/** @file
*
* ARM bit operations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/**
* Test and set bit atomically
*
* @v bit Bit to set
* @v bits Bit field
* @ret old Old value of bit (zero or non-zero)
*/
static inline __attribute__ (( always_inline )) int
test_and_set_bit ( unsigned int bit, volatile void *bits ) {
unsigned int index = ( bit / 64 );
unsigned int offset = ( bit % 64 );
volatile uint64_t *qword = ( ( ( volatile uint64_t * ) bits ) + index );
uint64_t mask = ( 1UL << offset );
uint64_t old;
uint64_t new;
uint32_t flag;
__asm__ __volatile__ ( "\n1:\n\t"
"ldxr %0, %3\n\t"
"orr %1, %0, %4\n\t"
"stxr %w2, %1, %3\n\t"
"tst %w2, %w2\n\t"
"bne 1b\n\t"
: "=&r" ( old ), "=&r" ( new ), "=&r" ( flag ),
"+Q" ( *qword )
: "r" ( mask )
: "cc" );
return ( !! ( old & mask ) );
}
/**
* Test and clear bit atomically
*
* @v bit Bit to set
* @v bits Bit field
* @ret old Old value of bit (zero or non-zero)
*/
static inline __attribute__ (( always_inline )) int
test_and_clear_bit ( unsigned int bit, volatile void *bits ) {
unsigned int index = ( bit / 64 );
unsigned int offset = ( bit % 64 );
volatile uint64_t *qword = ( ( ( volatile uint64_t * ) bits ) + index );
uint64_t mask = ( 1UL << offset );
uint64_t old;
uint64_t new;
uint32_t flag;
__asm__ __volatile__ ( "\n1:\n\t"
"ldxr %0, %3\n\t"
"bic %1, %0, %4\n\t"
"stxr %w2, %1, %3\n\t"
"tst %w2, %w2\n\t"
"bne 1b\n\t"
: "=&r" ( old ), "=&r" ( new ), "=&r" ( flag ),
"+Q" ( *qword )
: "r" ( mask )
: "cc" );
return ( !! ( old & mask ) );
}
/**
* Set bit atomically
*
* @v bit Bit to set
* @v bits Bit field
*/
static inline __attribute__ (( always_inline )) void
set_bit ( unsigned int bit, volatile void *bits ) {
test_and_set_bit ( bit, bits );
}
/**
* Clear bit atomically
*
* @v bit Bit to set
* @v bits Bit field
*/
static inline __attribute__ (( always_inline )) void
clear_bit ( unsigned int bit, volatile void *bits ) {
test_and_clear_bit ( bit, bits );
}
#endif /* _BITS_BITOPS_H */

View File

@@ -0,0 +1,47 @@
#ifndef _BITS_BYTESWAP_H
#define _BITS_BYTESWAP_H
/** @file
*
* Byte-order swapping functions
*
*/
#include <stdint.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static inline __attribute__ (( always_inline, const )) uint16_t
__bswap_variable_16 ( uint16_t x ) {
__asm__ ( "rev16 %0, %1" : "=r" ( x ) : "r" ( x ) );
return x;
}
static inline __attribute__ (( always_inline )) void
__bswap_16s ( uint16_t *x ) {
*x = __bswap_variable_16 ( *x );
}
static inline __attribute__ (( always_inline, const )) uint32_t
__bswap_variable_32 ( uint32_t x ) {
__asm__ ( "rev32 %0, %1" : "=r" ( x ) : "r" ( x ) );
return x;
}
static inline __attribute__ (( always_inline )) void
__bswap_32s ( uint32_t *x ) {
*x = __bswap_variable_32 ( *x );
}
static inline __attribute__ (( always_inline, const )) uint64_t
__bswap_variable_64 ( uint64_t x ) {
__asm__ ( "rev %0, %1" : "=r" ( x ) : "r" ( x ) );
return x;
}
static inline __attribute__ (( always_inline )) void
__bswap_64s ( uint64_t *x ) {
*x = __bswap_variable_64 ( *x );
}
#endif

View File

@@ -0,0 +1,16 @@
#ifndef _BITS_COMPILER_H
#define _BITS_COMPILER_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Dummy relocation type */
#define RELOC_TYPE_NONE R_AARCH64_NULL
#ifndef ASSEMBLY
#define __asmcall
#define __libgcc
#endif /* ASSEMBLY */
#endif /*_BITS_COMPILER_H */

View File

@@ -0,0 +1,28 @@
#ifndef _BITS_PROFILE_H
#define _BITS_PROFILE_H
/** @file
*
* Profiling
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/**
* Get profiling timestamp
*
* @ret timestamp Timestamp
*/
static inline __attribute__ (( always_inline )) uint64_t
profile_timestamp ( void ) {
uint64_t cycles;
/* Read cycle counter */
__asm__ __volatile__ ( "mrs %0, CNTVCT_EL0\n\t" : "=r" ( cycles ) );
return cycles;
}
#endif /* _BITS_PROFILE_H */

View File

@@ -0,0 +1,21 @@
#ifndef _BITS_STDINT_H
#define _BITS_STDINT_H
typedef __SIZE_TYPE__ size_t;
typedef signed long ssize_t;
typedef signed long off_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef unsigned long physaddr_t;
typedef unsigned long intptr_t;
#endif /* _BITS_STDINT_H */

View File

@@ -0,0 +1,106 @@
#ifndef BITS_STRING_H
#define BITS_STRING_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* String functions
*
*/
extern void arm64_bzero ( void *dest, size_t len );
extern void arm64_memset ( void *dest, size_t len, int character );
extern void arm64_memcpy ( void *dest, const void *src, size_t len );
extern void arm64_memmove_forwards ( void *dest, const void *src, size_t len );
extern void arm64_memmove_backwards ( void *dest, const void *src, size_t len );
extern void arm64_memmove ( void *dest, const void *src, size_t len );
/**
* Fill memory region
*
* @v dest Destination region
* @v character Fill character
* @v len Length
* @ret dest Destination region
*/
static inline __attribute__ (( always_inline )) void *
memset ( void *dest, int character, size_t len ) {
/* Allow gcc to generate inline "stX xzr" instructions for
* small, constant lengths.
*/
if ( __builtin_constant_p ( character ) && ( character == 0 ) &&
__builtin_constant_p ( len ) && ( len <= 64 ) ) {
__builtin_memset ( dest, 0, len );
return dest;
}
/* For zeroing larger or non-constant lengths, use the
* optimised variable-length zeroing code.
*/
if ( __builtin_constant_p ( character ) && ( character == 0 ) ) {
arm64_bzero ( dest, len );
return dest;
}
/* Not necessarily zeroing: use basic variable-length code */
arm64_memset ( dest, len, character );
return dest;
}
/**
* Copy memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
* @ret dest Destination region
*/
static inline __attribute__ (( always_inline )) void *
memcpy ( void *dest, const void *src, size_t len ) {
/* Allow gcc to generate inline "ldX"/"stX" instructions for
* small, constant lengths.
*/
if ( __builtin_constant_p ( len ) && ( len <= 64 ) ) {
__builtin_memcpy ( dest, src, len );
return dest;
}
/* Otherwise, use variable-length code */
arm64_memcpy ( dest, src, len );
return dest;
}
/**
* Copy (possibly overlapping) memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
* @ret dest Destination region
*/
static inline __attribute__ (( always_inline )) void *
memmove ( void *dest, const void *src, size_t len ) {
ssize_t offset = ( dest - src );
/* If required direction of copy is known at build time, then
* use the appropriate forwards/backwards copy directly.
*/
if ( __builtin_constant_p ( offset ) ) {
if ( offset <= 0 ) {
arm64_memmove_forwards ( dest, src, len );
return dest;
} else {
arm64_memmove_backwards ( dest, src, len );
return dest;
}
}
/* Otherwise, use ambidirectional copy */
arm64_memmove ( dest, src, len );
return dest;
}
#endif /* BITS_STRING_H */

View File

@@ -0,0 +1,69 @@
#ifndef _BITS_STRINGS_H
#define _BITS_STRINGS_H
/** @file
*
* String functions
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Find first (i.e. least significant) set bit
*
* @v value Value
* @ret lsb Least significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){
unsigned long long bits = value;
unsigned long long lsb;
unsigned int lz;
/* Extract least significant set bit */
lsb = ( bits & -bits );
/* Count number of leading zeroes before LSB */
__asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( lsb ) );
return ( 64 - lz );
}
/**
* Find first (i.e. least significant) set bit
*
* @v value Value
* @ret lsb Least significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __ffsl ( long value ) {
return __ffsll ( value );
}
/**
* Find last (i.e. most significant) set bit
*
* @v value Value
* @ret msb Most significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __flsll ( long long value ){
unsigned int lz;
/* Count number of leading zeroes */
__asm__ ( "clz %0, %1" : "=r" ( lz ) : "r" ( value ) );
return ( 64 - lz );
}
/**
* Find last (i.e. most significant) set bit
*
* @v value Value
* @ret msb Most significant bit set in value (LSB=1), or zero
*/
static inline __attribute__ (( always_inline )) int __flsl ( long value ) {
return __flsll ( value );
}
#endif /* _BITS_STRINGS_H */

View File

@@ -0,0 +1,15 @@
#ifndef _BITS_TCPIP_H
#define _BITS_TCPIP_H
/** @file
*
* Transport-network layer interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
extern uint16_t tcpip_continue_chksum ( uint16_t sum, const void *data,
size_t len );
#endif /* _BITS_TCPIP_H */

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
#ifndef _DHCP_ARCH_H
#define _DHCP_ARCH_H
/** @file
*
* Architecture-specific DHCP options
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/dhcp.h>
#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM64
#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
#endif

View File

@@ -0,0 +1,45 @@
#ifndef GDBMACH_H
#define GDBMACH_H
/** @file
*
* GDB architecture specifics
*
* This file declares functions for manipulating the machine state and
* debugging context.
*
*/
#include <stdint.h>
typedef unsigned long gdbreg_t;
/* Register snapshot */
enum {
/* Not yet implemented */
GDBMACH_NREGS,
};
#define GDBMACH_SIZEOF_REGS ( GDBMACH_NREGS * sizeof ( gdbreg_t ) )
static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
/* Not yet implemented */
( void ) regs;
( void ) pc;
}
static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) {
/* Not yet implemented */
( void ) regs;
( void ) step;
}
static inline void gdbmach_breakpoint ( void ) {
/* Not yet implemented */
}
extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
int enable );
extern void gdbmach_init ( void );
#endif /* GDBMACH_H */

View File

@@ -0,0 +1,59 @@
#ifndef LIMITS_H
#define LIMITS_H 1
/* Number of bits in a `char' */
#define CHAR_BIT 8
/* Minimum and maximum values a `signed char' can hold */
#define SCHAR_MIN (-128)
#define SCHAR_MAX 127
/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
#define UCHAR_MAX 255
/* Minimum and maximum values a `char' can hold */
#define CHAR_MIN SCHAR_MIN
#define CHAR_MAX SCHAR_MAX
/* Minimum and maximum values a `signed short int' can hold */
#define SHRT_MIN (-32768)
#define SHRT_MAX 32767
/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
#define USHRT_MAX 65535
/* Minimum and maximum values a `signed int' can hold */
#define INT_MIN (-INT_MAX - 1)
#define INT_MAX 2147483647
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed int' can hold */
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed long' can hold */
#define LONG_MAX 9223372036854775807L
#define LONG_MIN (-LONG_MAX - 1L)
/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
#define ULONG_MAX 18446744073709551615UL
/* Minimum and maximum values a `signed long long' can hold */
#define LLONG_MAX 9223372036854775807LL
#define LLONG_MIN (-LONG_MAX - 1LL)
/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
#define ULLONG_MAX 18446744073709551615ULL
#endif /* LIMITS_H */

View File

@@ -0,0 +1,44 @@
#ifndef _SETJMP_H
#define _SETJMP_H
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/** A jump buffer */
typedef struct {
/** Saved x19 */
uint64_t x19;
/** Saved x20 */
uint64_t x20;
/** Saved x21 */
uint64_t x21;
/** Saved x22 */
uint64_t x22;
/** Saved x23 */
uint64_t x23;
/** Saved x24 */
uint64_t x24;
/** Saved x25 */
uint64_t x25;
/** Saved x26 */
uint64_t x26;
/** Saved x27 */
uint64_t x27;
/** Saved x28 */
uint64_t x28;
/** Saved frame pointer (x29) */
uint64_t x29;
/** Saved link register (x30) */
uint64_t x30;
/** Saved stack pointer (x31) */
uint64_t sp;
} jmp_buf[1];
extern int __asmcall __attribute__ (( returns_twice ))
setjmp ( jmp_buf env );
extern void __asmcall __attribute__ (( noreturn ))
longjmp ( jmp_buf env, int val );
#endif /* _SETJMP_H */

View File

@@ -0,0 +1,49 @@
#ifndef _BITS_HYPERV_H
#define _BITS_HYPERV_H
/** @file
*
* Hyper-V interface
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdint.h>
#include <ipxe/io.h>
/**
* Issue hypercall
*
* @v hv Hyper-V hypervisor
* @v code Call code
* @v in Input parameters
* @v out Output parameters
* @ret status Status code
*/
static inline __attribute__ (( always_inline )) int
hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in,
void *out ) {
void *hypercall = hv->hypercall;
uint32_t in_phys;
uint32_t out_phys;
uint32_t discard_ecx;
uint32_t discard_edx;
uint16_t result;
in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) )
? 0 : virt_to_phys ( in ) );
out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) )
? 0 : virt_to_phys ( out ) );
__asm__ __volatile__ ( "call *%9"
: "=a" ( result ), "=c" ( discard_ecx ),
"=d" ( discard_edx )
: "d" ( 0 ), "a" ( code ),
"b" ( 0 ), "c" ( in_phys ),
"D" ( 0 ), "S" ( out_phys ),
"m" ( hypercall ) );
return result;
}
#endif /* _BITS_HYPERV_H */

View File

@@ -0,0 +1,38 @@
#ifndef _IPXE_MSR_H
#define _IPXE_MSR_H
/** @file
*
* Model-specific registers
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* Read model-specific register
*
* @v msr Model-specific register
* @ret value Value
*/
static inline __attribute__ (( always_inline )) uint64_t
rdmsr ( unsigned int msr ) {
uint64_t value;
__asm__ __volatile__ ( "rdmsr" : "=A" ( value ) : "c" ( msr ) );
return value;
}
/**
* Write model-specific register
*
* @v msr Model-specific register
* @v value Value
*/
static inline __attribute__ (( always_inline )) void
wrmsr ( unsigned int msr, uint64_t value ) {
__asm__ __volatile__ ( "wrmsr" : : "c" ( msr ), "A" ( value ) );
}
#endif /* _IPXE_MSR_H */

View File

@@ -0,0 +1,54 @@
.arch i386
.section ".data", "aw", @progbits
watch_me:
.long 0xfeedbeef
.section ".text", "ax", @progbits
.code32
gdbstub_test:
/* 1. Read registers test */
movl $0xea010203, %eax
movl $0xeb040506, %ebx
movl $0xec070809, %ecx
movl $0xed0a0b0c, %edx
movl $0x510d0e0f, %esi
movl $0xd1102030, %edi
int $3
/* 2. Write registers test */
int $3
/* 3. Read memory test */
subl $8, %esp
movl $0x11223344, 4(%esp)
movw $0x5566, 2(%esp)
movb $0x77, (%esp)
int $3
/* 4. Write memory test */
int $3
addl $8, %esp
/* 5. Step test */
int $3
nop
/* 6. Access watch test */
movl $0x600d0000, %ecx
movl watch_me, %eax
movl $0xbad00000, %ecx
int $3
movl $0x600d0001, %ecx
movl %eax, watch_me
movl $0xbad00001, %ecx
int $3
/* 7. Write watch test */
movl $0x600d0002, %ecx
movl %eax, watch_me
movl $0xbad00002, %ecx
int $3
1:
jmp 1b

View File

@@ -0,0 +1,116 @@
#!/usr/bin/gdb -x
# Test suite for GDB remote debugging
# Run:
# make bin/ipxe.hd.tmp
# make
# gdb
# (gdb) target remote :TCPPORT
# OR
# (gdb) target remote udp:IP:UDPPORT
# (gdb) source tests/gdbstub_test.gdb
define ipxe_load_symbols
file bin/ipxe.hd.tmp
end
define ipxe_assert
if $arg0 != $arg1
echo FAIL $arg2\n
else
echo PASS $arg2\n
end
end
define ipxe_start_tests
jump gdbstub_test
end
define ipxe_test_regs_read
ipxe_assert $eax 0xea010203 "ipxe_test_regs_read eax"
ipxe_assert $ebx 0xeb040506 "ipxe_test_regs_read ebx"
ipxe_assert $ecx 0xec070809 "ipxe_test_regs_read ecx"
ipxe_assert $edx 0xed0a0b0c "ipxe_test_regs_read edx"
ipxe_assert $esi 0x510d0e0f "ipxe_test_regs_read esi"
ipxe_assert $edi 0xd1102030 "ipxe_test_regs_read edi"
end
define ipxe_test_regs_write
set $eax = 0xea112233
set $ebx = 0xeb445566
set $ecx = 0xec778899
set $edx = 0xedaabbcc
set $esi = 0x51ddeeff
set $edi = 0xd1010203
c
ipxe_assert $eax 0xea112233 "ipxe_test_regs_write eax"
ipxe_assert $ebx 0xeb445566 "ipxe_test_regs_write ebx"
ipxe_assert $ecx 0xec778899 "ipxe_test_regs_write ecx"
ipxe_assert $edx 0xedaabbcc "ipxe_test_regs_write edx"
ipxe_assert $esi 0x51ddeeff "ipxe_test_regs_write esi"
ipxe_assert $edi 0xd1010203 "ipxe_test_regs_write edi"
# This assumes segment selectors are always 0x10 or 0x8 (for code).
ipxe_assert $cs 0x08 "ipxe_test_regs_write cs"
ipxe_assert $ds 0x10 "ipxe_test_regs_write ds"
end
define ipxe_test_mem_read
c
ipxe_assert ({int}($esp+4)) 0x11223344 "ipxe_test_mem_read int"
ipxe_assert ({short}($esp+2)) 0x5566 "ipxe_test_mem_read short"
ipxe_assert ({char}($esp)) 0x77 "ipxe_test_mem_read char"
end
define ipxe_test_mem_write
set ({int}($esp+4)) = 0xaabbccdd
set ({short}($esp+2)) = 0xeeff
set ({char}($esp)) = 0x99
c
ipxe_assert ({int}($esp+4)) 0xaabbccdd "ipxe_test_mem_write int"
ipxe_assert ({short}($esp+2)) (short)0xeeff "ipxe_test_mem_write short"
ipxe_assert ({char}($esp)) (char)0x99 "ipxe_test_mem_write char"
end
define ipxe_test_step
c
si
ipxe_assert ({char}($eip-1)) (char)0x90 "ipxe_test_step" # nop = 0x90
end
define ipxe_test_awatch
awatch watch_me
c
ipxe_assert $ecx 0x600d0000 "ipxe_test_awatch read"
if $ecx == 0x600d0000
c
end
c
ipxe_assert $ecx 0x600d0001 "ipxe_test_awatch write"
if $ecx == 0x600d0001
c
end
delete
end
define ipxe_test_watch
watch watch_me
c
ipxe_assert $ecx 0x600d0002 "ipxe_test_watch"
if $ecx == 0x600d0002
c
end
delete
end
ipxe_load_symbols
ipxe_start_tests
ipxe_test_regs_read
ipxe_test_regs_write
ipxe_test_mem_read
ipxe_test_mem_write
ipxe_test_step
ipxe_test_awatch
ipxe_test_watch

View File

@@ -0,0 +1,132 @@
# -*- makefile -*- : Force emacs to use Makefile mode
# BIOS-specific directories containing source files
#
SRCDIRS += arch/x86/drivers/net
# The i386 linker script
#
LDSCRIPT = arch/x86/scripts/pcbios.lds
# Stop ld from complaining about our customised linker script
#
LDFLAGS += -N --no-check-sections
# Prefix always starts at address zero
#
LDFLAGS += --section-start=.prefix=0
# Media types.
#
MEDIA += rom
MEDIA += mrom
MEDIA += pcirom
MEDIA += isarom
MEDIA += pxe
MEDIA += kpxe
MEDIA += kkpxe
MEDIA += kkkpxe
MEDIA += lkrn
MEDIA += dsk
MEDIA += nbi
MEDIA += hd
MEDIA += raw
MEDIA += exe
# Padding rules
#
PAD_rom = $(PERL) $(PADIMG) --blksize=512 --byte=0xff
PAD_mrom = $(PAD_rom)
PAD_pcirom = $(PAD_rom)
PAD_isarom = $(PAD_rom)
PAD_dsk = $(PERL) $(PADIMG) --blksize=512
PAD_hd = $(PERL) $(PADIMG) --blksize=32768
PAD_exe = $(PERL) $(PADIMG) --blksize=512
# Finalisation rules
#
FINALISE_rom = $(PERL) $(FIXROM)
FINALISE_mrom = $(FINALISE_rom)
FINALISE_pcirom = $(FINALISE_rom)
FINALISE_isarom = $(FINALISE_rom)
# Use $(ROMS) rather than $(DRIVERS) for "allroms", "allmroms", etc.
#
LIST_NAME_rom := ROMS
LIST_NAME_mrom := ROMS
LIST_NAME_pcirom := ROMS
LIST_NAME_isarom := ROMS
# Locations of isolinux files
#
SYSLINUX_DIR_LIST := \
/usr/lib/syslinux \
/usr/lib/syslinux/bios \
/usr/lib/syslinux/modules/bios \
/usr/share/syslinux \
/usr/share/syslinux/bios \
/usr/share/syslinux/modules/bios \
/usr/local/share/syslinux \
/usr/local/share/syslinux/bios \
/usr/local/share/syslinux/modules/bios \
/usr/lib/ISOLINUX
ISOLINUX_BIN_LIST := \
$(ISOLINUX_BIN) \
$(patsubst %,%/isolinux.bin,$(SYSLINUX_DIR_LIST))
LDLINUX_C32_LIST := \
$(LDLINUX_C32) \
$(patsubst %,%/ldlinux.c32,$(SYSLINUX_DIR_LIST))
ISOLINUX_BIN = $(firstword $(wildcard $(ISOLINUX_BIN_LIST)))
LDLINUX_C32 = $(firstword $(wildcard $(LDLINUX_C32_LIST)))
# rule to make a non-emulation ISO boot image
NON_AUTO_MEDIA += iso
%iso: %lkrn util/geniso
$(QM)$(ECHO) " [GENISO] $@"
$(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) LDLINUX_C32=$(LDLINUX_C32) \
VERSION="$(VERSION)" bash util/geniso -o $@ $<
# rule to make a floppy emulation ISO boot image
NON_AUTO_MEDIA += liso
%liso: %lkrn util/geniso
$(QM)$(ECHO) " [GENISO] $@"
$(Q)VERSION="$(VERSION)" bash util/geniso -l -o $@ $<
# rule to make a syslinux floppy image (mountable, bootable)
NON_AUTO_MEDIA += sdsk
%sdsk: %lkrn util/gensdsk
$(QM)$(ECHO) " [GENSDSK] $@"
$(Q)bash util/gensdsk $@ $<
# rule to write disk images to /dev/fd0
NON_AUTO_MEDIA += fd0
%fd0 : %dsk
$(QM)$(ECHO) " [DD] $@"
$(Q)dd if=$< bs=512 conv=sync of=/dev/fd0
$(Q)sync
# Special target for building Master Boot Record binary
$(BIN)/mbr.tmp : $(BIN)/mbr.o
$(QM)$(ECHO) " [LD] $@"
$(Q)$(LD) $(LDFLAGS) -o $@ -e mbr $<
# rule to make a USB disk image
$(BIN)/usbdisk.tmp : $(BIN)/usbdisk.o
$(QM)$(ECHO) " [LD] $@"
$(Q)$(LD) $(LDFLAGS) -o $@ -e mbr $<
NON_AUTO_MEDIA += usb
%usb: $(BIN)/usbdisk.bin %hd
$(QM)$(ECHO) " [FINISH] $@"
$(Q)cat $^ > $@
NON_AUTO_MEDIA += vhd
%vhd: %usb
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(QEMUIMG) convert -f raw -O vpc $< $@
# Padded floppy image (e.g. for iLO)
NON_AUTO_MEDIA += pdsk
%pdsk : %dsk
$(Q)cp $< $@
$(Q)$(PADIMG) --blksize=1474560 $@

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* Packet buffer in base memory. Used by various components which
* need to pass packets to and from external real-mode code.
*
*/
#include <basemem_packet.h>
#undef basemem_packet
char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] );

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <realmode.h>
#include <pxe_api.h>
/** @file
*
* Cached DHCP packet
*
*/
/** Cached DHCPACK physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cached_dhcpack_phys );
#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
/** Colour for debug messages */
#define colour &cached_dhcpack_phys
/** Cached DHCPACK */
static struct dhcp_packet *cached_dhcpack;
/**
* Cached DHCPACK startup function
*
*/
static void cachedhcp_init ( void ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
size_t max_len;
size_t len;
/* Do nothing if no cached DHCPACK is present */
if ( ! cached_dhcpack_phys ) {
DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
return;
}
/* No reliable way to determine length before parsing packet;
* start by assuming maximum length permitted by PXE.
*/
max_len = sizeof ( BOOTPLAYER_t );
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
if ( ! dhcppkt ) {
DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
return;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
max_len );
dhcppkt_init ( dhcppkt, dhcphdr, max_len );
/* Shrink packet to required length. If reallocation fails,
* just continue to use the original packet and waste the
* unused space.
*/
len = dhcppkt_len ( dhcppkt );
assert ( len <= max_len );
tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) );
if ( tmp )
dhcppkt = tmp;
/* Reinitialise packet at new address */
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
dhcppkt_init ( dhcppkt, dhcphdr, len );
/* Store as cached DHCPACK, and mark original copy as consumed */
DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n",
cached_dhcpack_phys, len );
cached_dhcpack = dhcppkt;
cached_dhcpack_phys = 0;
}
/**
* Cached DHCPACK startup function
*
*/
static void cachedhcp_startup ( void ) {
/* If cached DHCP packet was not claimed by any network device
* during startup, then free it.
*/
if ( cached_dhcpack ) {
DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
}
}
/** Cached DHCPACK initialisation function */
struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = cachedhcp_init,
};
/** Cached DHCPACK startup function */
struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
.name = "cachedhcp",
.startup = cachedhcp_startup,
};
/**
* Apply cached DHCPACK to network device, if applicable
*
* @v netdev Network device
* @ret rc Return status code
*/
static int cachedhcp_probe ( struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
int rc;
/* Do nothing unless we have a cached DHCPACK */
if ( ! cached_dhcpack )
return 0;
/* Do nothing unless cached DHCPACK's MAC address matches this
* network device.
*/
if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
ll_protocol->ll_addr_len ) != 0 ) {
DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
netdev->name );
return 0;
}
DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
/* Register as DHCP settings for this network device */
if ( ( rc = register_settings ( &cached_dhcpack->settings,
netdev_settings ( netdev ),
DHCP_SETTINGS_NAME ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
strerror ( rc ) );
return rc;
}
/* Claim cached DHCPACK */
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
return 0;
}
/** Cached DHCP packet network device driver */
struct net_driver cachedhcp_driver __net_driver = {
.name = "cachedhcp",
.probe = cachedhcp_probe,
};

View File

@@ -0,0 +1,20 @@
#include <stdio.h>
#include <realmode.h>
void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) {
__asm__ __volatile__ (
TEXT16_CODE ( ".globl dump_regs\n\t"
"\ndump_regs:\n\t"
VIRT_CALL ( _dump_regs )
"ret\n\t" ) : );
printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
"CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x\n",
ix86->regs.eax, ix86->regs.ebx, ix86->regs.ecx,
ix86->regs.edx, ix86->regs.esi, ix86->regs.edi,
ix86->regs.ebp, ix86->regs.esp,
ix86->segs.cs, ix86->segs.ss, ix86->segs.ds,
ix86->segs.es, ix86->segs.fs, ix86->segs.gs );
}

251
src/arch/x86/core/gdbmach.c Normal file
View File

@@ -0,0 +1,251 @@
/*
* Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/uaccess.h>
#include <ipxe/gdbstub.h>
#include <librm.h>
#include <gdbmach.h>
/** @file
*
* GDB architecture-specific bits for x86
*
*/
/** Number of hardware breakpoints */
#define NUM_HWBP 4
/** Debug register 7: Global breakpoint enable */
#define DR7_G( bp ) ( 2 << ( 2 * (bp) ) )
/** Debug register 7: Global exact breakpoint enable */
#define DR7_GE ( 1 << 9 )
/** Debug register 7: Break on data writes */
#define DR7_RWLEN_WRITE 0x11110000
/** Debug register 7: Break on data access */
#define DR7_RWLEN_ACCESS 0x33330000
/** Debug register 7: One-byte length */
#define DR7_RWLEN_1 0x00000000
/** Debug register 7: Two-byte length */
#define DR7_RWLEN_2 0x44440000
/** Debug register 7: Four-byte length */
#define DR7_RWLEN_4 0xcccc0000
/** Debug register 7: Eight-byte length */
#define DR7_RWLEN_8 0x88880000
/** Debug register 7: Breakpoint R/W and length mask */
#define DR7_RWLEN_MASK( bp ) ( 0xf0000 << ( 4 * (bp) ) )
/** Hardware breakpoint addresses (debug registers 0-3) */
static unsigned long dr[NUM_HWBP];
/** Active value of debug register 7 */
static unsigned long dr7 = DR7_GE;
/**
* Update debug registers
*
*/
static void gdbmach_update ( void ) {
/* Set debug registers */
__asm__ __volatile__ ( "mov %0, %%dr0" : : "r" ( dr[0] ) );
__asm__ __volatile__ ( "mov %0, %%dr1" : : "r" ( dr[1] ) );
__asm__ __volatile__ ( "mov %0, %%dr2" : : "r" ( dr[2] ) );
__asm__ __volatile__ ( "mov %0, %%dr3" : : "r" ( dr[3] ) );
__asm__ __volatile__ ( "mov %0, %%dr7" : : "r" ( dr7 ) );
}
/**
* Find reusable or available hardware breakpoint
*
* @v addr Linear address
* @v rwlen Control bits
* @ret bp Hardware breakpoint, or negative error
*/
static int gdbmach_find ( unsigned long addr, unsigned int rwlen ) {
unsigned int i;
int bp = -ENOENT;
/* Look for a reusable or available breakpoint */
for ( i = 0 ; i < NUM_HWBP ; i++ ) {
/* If breakpoint is not enabled, then it is available */
if ( ! ( dr7 & DR7_G ( i ) ) ) {
bp = i;
continue;
}
/* If breakpoint is enabled and has the same address
* and control bits, then reuse it.
*/
if ( ( dr[i] == addr ) &&
( ( ( dr7 ^ rwlen ) & DR7_RWLEN_MASK ( i ) ) == 0 ) ) {
bp = i;
break;
}
}
return bp;
}
/**
* Set hardware breakpoint
*
* @v type GDB breakpoint type
* @v addr Virtual address
* @v len Length
* @v enable Enable (not disable) breakpoint
* @ret rc Return status code
*/
int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
int enable ) {
unsigned int rwlen;
unsigned long mask;
int bp;
/* Parse breakpoint type */
switch ( type ) {
case GDBMACH_WATCH:
rwlen = DR7_RWLEN_WRITE;
break;
case GDBMACH_AWATCH:
rwlen = DR7_RWLEN_ACCESS;
break;
default:
return -ENOTSUP;
}
/* Parse breakpoint length */
switch ( len ) {
case 1:
rwlen |= DR7_RWLEN_1;
break;
case 2:
rwlen |= DR7_RWLEN_2;
break;
case 4:
rwlen |= DR7_RWLEN_4;
break;
case 8:
rwlen |= DR7_RWLEN_8;
break;
default:
return -ENOTSUP;
}
/* Convert to linear address */
if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) )
addr = virt_to_phys ( ( void * ) addr );
/* Find reusable or available hardware breakpoint */
bp = gdbmach_find ( addr, rwlen );
if ( bp < 0 )
return ( enable ? -ENOBUFS : 0 );
/* Configure this breakpoint */
DBGC ( &dr[0], "GDB bp %d at %p+%zx type %d (%sabled)\n",
bp, ( ( void * ) addr ), len, type, ( enable ? "en" : "dis" ) );
dr[bp] = addr;
mask = DR7_RWLEN_MASK ( bp );
dr7 = ( ( dr7 & ~mask ) | ( rwlen & mask ) );
mask = DR7_G ( bp );
dr7 &= ~mask;
if ( enable )
dr7 |= mask;
/* Update debug registers */
gdbmach_update();
return 0;
}
/**
* Handle exception
*
* @v signo GDB signal number
* @v regs Register dump
*/
__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
unsigned long dr7_disabled = DR7_GE;
unsigned long dr6_clear = 0;
/* Temporarily disable breakpoints */
__asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7_disabled ) );
/* Handle exception */
DBGC ( &dr[0], "GDB signal %d\n", signo );
DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
gdbstub_handler ( signo, regs );
DBGC ( &dr[0], "GDB signal %d returning\n", signo );
DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
/* Clear breakpoint status register */
__asm__ __volatile__ ( "mov %0, %%dr6\n" : : "r" ( dr6_clear ) );
/* Re-enable breakpoints */
__asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7 ) );
}
/**
* CPU exception vectors
*
* Note that we cannot intercept anything from INT8 (double fault)
* upwards, since these overlap by default with IRQ0-7.
*/
static void * gdbmach_vectors[] = {
gdbmach_sigfpe, /* Divide by zero */
gdbmach_sigtrap, /* Debug trap */
NULL, /* Non-maskable interrupt */
gdbmach_sigtrap, /* Breakpoint */
gdbmach_sigstkflt, /* Overflow */
gdbmach_sigstkflt, /* Bound range exceeded */
gdbmach_sigill, /* Invalid opcode */
};
/**
* Initialise GDB
*/
void gdbmach_init ( void ) {
unsigned int i;
/* Hook CPU exception vectors */
for ( i = 0 ; i < ( sizeof ( gdbmach_vectors ) /
sizeof ( gdbmach_vectors[0] ) ) ; i++ ) {
if ( gdbmach_vectors[i] )
set_interrupt_vector ( i, gdbmach_vectors[i] );
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2009 H. Peter Anvin <hpa@zytor.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.arch i386
.code16
/****************************************************************************
* Set/clear CF on the stack as appropriate, assumes stack is as it should
* be immediately before IRET
****************************************************************************
*/
.section ".text16", "ax", @progbits
.globl patch_cf
patch_cf:
pushw %bp
movw %sp, %bp
setc 8(%bp) /* Set/reset CF; clears PF, AF, ZF, SF */
popw %bp
ret
.size patch_cf, . - patch_cf

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2014 Red Hat Inc.
* Alex Williamson <alex.williamson@redhat.com>
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/device.h>
#include <ipxe/init.h>
#include <realmode.h>
#include <usr/autoboot.h>
uint16_t __bss16 ( autoboot_busdevfn );
#define autoboot_busdevfn __use_data16 ( autoboot_busdevfn )
/**
* Initialise PCI autoboot device
*/
static void pci_autoboot_init ( void ) {
if ( autoboot_busdevfn )
set_autoboot_busloc ( BUS_TYPE_PCI, autoboot_busdevfn );
}
/** PCI autoboot device initialisation function */
struct init_fn pci_autoboot_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = pci_autoboot_init,
};

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/io.h>
#include <pic8259.h>
/** @file
*
* Minimal support for the 8259 Programmable Interrupt Controller
*
*/
/**
* Send non-specific EOI(s)
*
* @v irq IRQ number
*
* This seems to be inherently unsafe.
*/
static inline void send_nonspecific_eoi ( unsigned int irq ) {
DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
if ( irq >= IRQ_PIC_CUTOFF ) {
outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
}
outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
}
/**
* Send specific EOI(s)
*
* @v irq IRQ number
*/
static inline void send_specific_eoi ( unsigned int irq ) {
DBG ( "Sending specific EOI for IRQ %d\n", irq );
if ( irq >= IRQ_PIC_CUTOFF ) {
outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ),
ICR_REG ( CHAINED_IRQ ) );
}
outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) );
}
/**
* Send End-Of-Interrupt to the PIC
*
* @v irq IRQ number
*/
void send_eoi ( unsigned int irq ) {
send_specific_eoi ( irq );
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <assert.h>
#include <ipxe/io.h>
#include <ipxe/pit8254.h>
/** @file
*
* 8254 Programmable Interval Timer
*
*/
/**
* Delay for a fixed number of timer ticks using the speaker channel
*
* @v ticks Number of timer ticks for which to delay
*/
void pit8254_speaker_delay ( unsigned int ticks ) {
uint8_t spkr;
uint8_t cmd;
uint8_t low;
uint8_t high;
/* Sanity check */
assert ( ticks <= 0xffff );
/* Disable speaker, set speaker channel gate input high */
spkr = inb ( PIT8254_SPKR );
spkr &= ~PIT8254_SPKR_ENABLE;
spkr |= PIT8254_SPKR_GATE;
outb ( spkr, PIT8254_SPKR );
/* Program speaker channel to "interrupt" on terminal count */
cmd = ( PIT8254_CMD_CHANNEL ( PIT8254_CH_SPKR ) |
PIT8254_CMD_ACCESS_LOHI | PIT8254_CMD_OP_TERMINAL |
PIT8254_CMD_BINARY );
low = ( ( ticks >> 0 ) & 0xff );
high = ( ( ticks >> 8 ) & 0xff );
outb ( cmd, PIT8254_CMD );
outb ( low, PIT8254_DATA ( PIT8254_CH_SPKR ) );
outb ( high, PIT8254_DATA ( PIT8254_CH_SPKR ) );
/* Wait for channel to "interrupt" */
do {
spkr = inb ( PIT8254_SPKR );
} while ( ! ( spkr & PIT8254_SPKR_OUT ) );
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* RDTSC timer
*
*/
#include <string.h>
#include <errno.h>
#include <ipxe/timer.h>
#include <ipxe/cpuid.h>
#include <ipxe/pit8254.h>
/** Number of microseconds to use for TSC calibration */
#define TSC_CALIBRATE_US 1024
/** TSC increment per microsecond */
static unsigned long tsc_per_us;
/** Minimum resolution for scaled TSC timer */
#define TSC_SCALED_HZ 32
/** TSC scale (expressed as a bit shift)
*
* We use this to avoid the need for 64-bit divsion on 32-bit systems.
*/
static unsigned int tsc_scale;
/** Number of timer ticks per scaled TSC increment */
static unsigned long ticks_per_scaled_tsc;
/** Colour for debug messages */
#define colour &tsc_per_us
/**
* Get raw TSC value
*
* @ret tsc Raw TSC value
*/
static inline __always_inline unsigned long rdtsc_raw ( void ) {
unsigned long raw;
__asm__ __volatile__ ( "rdtsc\n\t" : "=a" ( raw ) : : "edx" );
return raw;
}
/**
* Get TSC value, shifted to avoid rollover within a realistic timescale
*
* @ret tsc Scaled TSC value
*/
static inline __always_inline unsigned long rdtsc_scaled ( void ) {
unsigned long scaled;
__asm__ __volatile__ ( "rdtsc\n\t"
"shrdl %b1, %%edx, %%eax\n\t"
: "=a" ( scaled ) : "c" ( tsc_scale ) : "edx" );
return scaled;
}
/**
* Get current system time in ticks
*
* @ret ticks Current time, in ticks
*/
static unsigned long rdtsc_currticks ( void ) {
unsigned long scaled;
scaled = rdtsc_scaled();
return ( scaled * ticks_per_scaled_tsc );
}
/**
* Delay for a fixed number of microseconds
*
* @v usecs Number of microseconds for which to delay
*/
static void rdtsc_udelay ( unsigned long usecs ) {
unsigned long start;
unsigned long elapsed;
unsigned long threshold;
start = rdtsc_raw();
threshold = ( usecs * tsc_per_us );
do {
elapsed = ( rdtsc_raw() - start );
} while ( elapsed < threshold );
}
/**
* Probe RDTSC timer
*
* @ret rc Return status code
*/
static int rdtsc_probe ( void ) {
unsigned long before;
unsigned long after;
unsigned long elapsed;
uint32_t apm;
uint32_t discard_a;
uint32_t discard_b;
uint32_t discard_c;
int rc;
/* Check that TSC is invariant */
if ( ( rc = cpuid_supported ( CPUID_APM ) ) != 0 ) {
DBGC ( colour, "RDTSC cannot determine APM features: %s\n",
strerror ( rc ) );
return rc;
}
cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm );
if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
apm );
return -ENOTTY;
}
/* Calibrate udelay() timer via 8254 PIT */
before = rdtsc_raw();
pit8254_udelay ( TSC_CALIBRATE_US );
after = rdtsc_raw();
elapsed = ( after - before );
tsc_per_us = ( elapsed / TSC_CALIBRATE_US );
if ( ! tsc_per_us ) {
DBGC ( colour, "RDTSC has zero TSC per microsecond\n" );
return -EIO;
}
/* Calibrate currticks() scaling factor */
tsc_scale = 31;
ticks_per_scaled_tsc = ( ( 1UL << tsc_scale ) /
( tsc_per_us * ( 1000000 / TICKS_PER_SEC ) ) );
while ( ticks_per_scaled_tsc > ( TICKS_PER_SEC / TSC_SCALED_HZ ) ) {
tsc_scale--;
ticks_per_scaled_tsc >>= 1;
}
DBGC ( colour, "RDTSC has %ld tsc per us, %ld ticks per 2^%d tsc\n",
tsc_per_us, ticks_per_scaled_tsc, tsc_scale );
if ( ! ticks_per_scaled_tsc ) {
DBGC ( colour, "RDTSC has zero ticks per TSC\n" );
return -EIO;
}
return 0;
}
/** RDTSC timer */
struct timer rdtsc_timer __timer ( TIMER_PREFERRED ) = {
.name = "rdtsc",
.probe = rdtsc_probe,
.currticks = rdtsc_currticks,
.udelay = rdtsc_udelay,
};

View File

@@ -0,0 +1,136 @@
#include <ipxe/io.h>
#include <registers.h>
/*
* Originally by Eric Biederman
*
* Heavily modified by Michael Brown
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* Linker symbols */
extern char _textdata[];
extern char _etextdata[];
/* within 1MB of 4GB is too close.
* MAX_ADDR is the maximum address we can easily do DMA to.
*
* Not sure where this constraint comes from, but kept it from Eric's
* old code - mcb30
*/
#define MAX_ADDR (0xfff00000UL)
/* Preserve alignment to a 4kB page
*
* Required for x86_64, and doesn't hurt for i386.
*/
#define ALIGN 4096
/**
* Relocate iPXE
*
* @v ebp Maximum address to use for relocation
* @ret esi Current physical address
* @ret edi New physical address
* @ret ecx Length to copy
*
* This finds a suitable location for iPXE near the top of 32-bit
* address space, and returns the physical address of the new location
* to the prefix in %edi.
*/
__asmcall void relocate ( struct i386_all_regs *ix86 ) {
struct memory_map memmap;
uint32_t start, end, size, padded_size, max;
uint32_t new_start, new_end;
unsigned i;
/* Get memory map and current location */
get_memmap ( &memmap );
start = virt_to_phys ( _textdata );
end = virt_to_phys ( _etextdata );
size = ( end - start );
padded_size = ( size + ALIGN - 1 );
DBG ( "Relocate: currently at [%x,%x)\n"
"...need %x bytes for %d-byte alignment\n",
start, end, padded_size, ALIGN );
/* Determine maximum usable address */
max = MAX_ADDR;
if ( ix86->regs.ebp < max ) {
max = ix86->regs.ebp;
DBG ( "Limiting relocation to [0,%x)\n", max );
}
/* Walk through the memory map and find the highest address
* below 4GB that iPXE will fit into.
*/
new_end = end;
for ( i = 0 ; i < memmap.count ; i++ ) {
struct memory_region *region = &memmap.regions[i];
uint32_t r_start, r_end;
DBG ( "Considering [%llx,%llx)\n", region->start, region->end);
/* Truncate block to maximum address. This will be
* less than 4GB, which means that we can get away
* with using just 32-bit arithmetic after this stage.
*/
if ( region->start > max ) {
DBG ( "...starts after max=%x\n", max );
continue;
}
r_start = region->start;
if ( region->end > max ) {
DBG ( "...end truncated to max=%x\n", max );
r_end = max;
} else {
r_end = region->end;
}
DBG ( "...usable portion is [%x,%x)\n", r_start, r_end );
/* If we have rounded down r_end below r_ start, skip
* this block.
*/
if ( r_end < r_start ) {
DBG ( "...truncated to negative size\n" );
continue;
}
/* Check that there is enough space to fit in iPXE */
if ( ( r_end - r_start ) < size ) {
DBG ( "...too small (need %x bytes)\n", size );
continue;
}
/* If the start address of the iPXE we would
* place in this block is higher than the end address
* of the current highest block, use this block.
*
* Note that this avoids overlaps with the current
* iPXE, as well as choosing the highest of all viable
* blocks.
*/
if ( ( r_end - size ) > new_end ) {
new_end = r_end;
DBG ( "...new best block found.\n" );
}
}
/* Calculate new location of iPXE, and align it to the
* required alignemnt.
*/
new_start = new_end - padded_size;
new_start += ( ( start - new_start ) & ( ALIGN - 1 ) );
new_end = new_start + size;
DBG ( "Relocating from [%x,%x) to [%x,%x)\n",
start, end, new_start, new_end );
/* Let prefix know what to copy */
ix86->regs.esi = start;
ix86->regs.edi = new_start;
ix86->regs.ecx = size;
}

270
src/arch/x86/core/runtime.c Normal file
View File

@@ -0,0 +1,270 @@
/*
* Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Command line and initrd passed to iPXE at runtime
*
*/
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/init.h>
#include <ipxe/image.h>
#include <ipxe/script.h>
#include <ipxe/umalloc.h>
#include <realmode.h>
/** Command line physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cmdline_phys );
#define cmdline_phys __use_data16 ( cmdline_phys )
/** initrd physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( initrd_phys );
#define initrd_phys __use_data16 ( initrd_phys )
/** initrd length
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( initrd_len );
#define initrd_len __use_data16 ( initrd_len )
/** Internal copy of the command line */
static char *cmdline_copy;
/** Free command line image */
static void cmdline_image_free ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt );
DBGC ( image, "RUNTIME freeing command line\n" );
free ( cmdline_copy );
}
/** Embedded script representing the command line */
static struct image cmdline_image = {
.refcnt = REF_INIT ( cmdline_image_free ),
.name = "<CMDLINE>",
.type = &script_image_type,
};
/** Colour for debug messages */
#define colour &cmdline_image
/**
* Strip unwanted cruft from command line
*
* @v cmdline Command line
* @v cruft Initial substring of cruft to strip
*/
static void cmdline_strip ( char *cmdline, const char *cruft ) {
char *strip;
char *strip_end;
/* Find unwanted cruft, if present */
if ( ! ( strip = strstr ( cmdline, cruft ) ) )
return;
/* Strip unwanted cruft */
strip_end = strchr ( strip, ' ' );
if ( strip_end ) {
*strip_end = '\0';
DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
strcpy ( strip, ( strip_end + 1 ) );
} else {
DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
*strip = '\0';
}
}
/**
* Initialise command line
*
* @ret rc Return status code
*/
static int cmdline_init ( void ) {
userptr_t cmdline_user;
char *cmdline;
size_t len;
int rc;
/* Do nothing if no command line was specified */
if ( ! cmdline_phys ) {
DBGC ( colour, "RUNTIME found no command line\n" );
return 0;
}
cmdline_user = phys_to_user ( cmdline_phys );
len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );
/* Allocate and copy command line */
cmdline_copy = malloc ( len );
if ( ! cmdline_copy ) {
DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
"command line\n", len );
rc = -ENOMEM;
goto err_alloc_cmdline_copy;
}
cmdline = cmdline_copy;
copy_from_user ( cmdline, cmdline_user, 0, len );
DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
cmdline, cmdline_phys );
/* Mark command line as consumed */
cmdline_phys = 0;
/* Strip unwanted cruft from the command line */
cmdline_strip ( cmdline, "BOOT_IMAGE=" );
cmdline_strip ( cmdline, "initrd=" );
while ( isspace ( *cmdline ) )
cmdline++;
DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline );
/* Prepare and register image */
cmdline_image.data = virt_to_user ( cmdline );
cmdline_image.len = strlen ( cmdline );
if ( cmdline_image.len ) {
if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) {
DBGC ( colour, "RUNTIME could not register command "
"line: %s\n", strerror ( rc ) );
goto err_register_image;
}
}
/* Drop our reference to the image */
image_put ( &cmdline_image );
return 0;
err_register_image:
image_put ( &cmdline_image );
err_alloc_cmdline_copy:
return rc;
}
/**
* Initialise initrd
*
* @ret rc Return status code
*/
static int initrd_init ( void ) {
struct image *image;
int rc;
/* Do nothing if no initrd was specified */
if ( ! initrd_phys ) {
DBGC ( colour, "RUNTIME found no initrd\n" );
return 0;
}
if ( ! initrd_len ) {
DBGC ( colour, "RUNTIME found empty initrd\n" );
return 0;
}
DBGC ( colour, "RUNTIME found initrd at [%x,%x)\n",
initrd_phys, ( initrd_phys + initrd_len ) );
/* Allocate image */
image = alloc_image ( NULL );
if ( ! image ) {
DBGC ( colour, "RUNTIME could not allocate image for "
"initrd\n" );
rc = -ENOMEM;
goto err_alloc_image;
}
if ( ( rc = image_set_name ( image, "<INITRD>" ) ) != 0 ) {
DBGC ( colour, "RUNTIME could not set image name: %s\n",
strerror ( rc ) );
goto err_set_name;
}
/* Allocate and copy initrd content */
image->data = umalloc ( initrd_len );
if ( ! image->data ) {
DBGC ( colour, "RUNTIME could not allocate %d bytes for "
"initrd\n", initrd_len );
rc = -ENOMEM;
goto err_umalloc;
}
image->len = initrd_len;
memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0,
initrd_len );
/* Mark initrd as consumed */
initrd_phys = 0;
/* Register image */
if ( ( rc = register_image ( image ) ) != 0 ) {
DBGC ( colour, "RUNTIME could not register initrd: %s\n",
strerror ( rc ) );
goto err_register_image;
}
/* Drop our reference to the image */
image_put ( image );
return 0;
err_register_image:
err_umalloc:
err_set_name:
image_put ( image );
err_alloc_image:
return rc;
}
/**
* Initialise command line and initrd
*
*/
static void runtime_init ( void ) {
int rc;
/* Initialise command line */
if ( ( rc = cmdline_init() ) != 0 ) {
/* No way to report failure */
return;
}
/* Initialise initrd */
if ( ( rc = initrd_init() ) != 0 ) {
/* No way to report failure */
return;
}
}
/** Command line and initrd initialisation function */
struct startup_fn runtime_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.name = "runtime",
.startup = runtime_init,
};

21
src/arch/x86/core/stack.S Normal file
View File

@@ -0,0 +1,21 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.arch i386
#ifdef __x86_64__
#define STACK_SIZE 8192
#else
#define STACK_SIZE 4096
#endif
/****************************************************************************
* Internal stack
****************************************************************************
*/
.section ".stack", "aw", @nobits
.align 8
.globl _stack
_stack:
.space STACK_SIZE
.globl _estack
_estack:

View File

@@ -0,0 +1,15 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.arch i386
/****************************************************************************
* Internal stack
****************************************************************************
*/
.section ".stack16", "aw", @nobits
.align 8
.globl _stack16
_stack16:
.space 4096
.globl _estack16
_estack16:

View File

@@ -0,0 +1,113 @@
/*
*
* modified from linuxbios code
* by Cai Qiang <rimy2000@hotmail.com>
*
*/
#include "stddef.h"
#include "string.h"
#include <ipxe/io.h>
#include <ipxe/console.h>
#include <ipxe/init.h>
#include "vga.h"
#include <config/console.h>
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_DIRECT_VGA ) && \
CONSOLE_EXPLICIT ( CONSOLE_DIRECT_VGA ) )
#undef CONSOLE_DIRECT_VGA
#define CONSOLE_DIRECT_VGA ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
struct console_driver vga_console __console_driver;
static char *vidmem; /* The video buffer */
static int video_line, video_col;
#define VIDBUFFER 0xB8000
static void memsetw(void *s, int c, unsigned int n)
{
unsigned int i;
u16 *ss = (u16 *) s;
for (i = 0; i < n; i++) {
ss[i] = ( u16 ) c;
}
}
static void video_init(void)
{
static int inited=0;
vidmem = (char *)phys_to_virt(VIDBUFFER);
if (!inited) {
video_line = 0;
video_col = 0;
memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); //
inited=1;
}
}
static void video_scroll(void)
{
int i;
memmove(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
vidmem[i] = ' ';
}
static void vga_putc(int byte)
{
if (byte == '\n') {
video_line++;
video_col = 0;
} else if (byte == '\r') {
video_col = 0;
} else if (byte == '\b') {
video_col--;
} else if (byte == '\t') {
video_col += 4;
} else if (byte == '\a') {
//beep
//beep(500);
} else {
vidmem[((video_col + (video_line *COLS)) * 2)] = byte;
vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT;
video_col++;
}
if (video_col < 0) {
video_col = 0;
}
if (video_col >= COLS) {
video_line++;
video_col = 0;
}
if (video_line >= LINES) {
video_scroll();
video_line--;
}
// move the cursor
write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI);
write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
}
struct console_driver vga_console __console_driver = {
.putchar = vga_putc,
.disabled = CONSOLE_DISABLED,
.usage = CONSOLE_DIRECT_VGA,
};
struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = {
.initialise = video_init,
};

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/uaccess.h>
#include <ipxe/settings.h>
/** @file
*
* Video RAM dump
*
*/
/** Video RAM base address */
#define VRAM_BASE 0xb8000
/** Video RAM length */
#define VRAM_LEN \
( 80 /* columns */ * 25 /* rows */ * 2 /* bytes per character */ )
/**
* Fetch video RAM setting
*
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int vram_fetch ( void *data, size_t len ) {
userptr_t vram = phys_to_user ( VRAM_BASE );
/* Copy video RAM */
if ( len > VRAM_LEN )
len = VRAM_LEN;
copy_from_user ( data, vram, 0, len );
return VRAM_LEN;
}
/** Video RAM setting */
const struct setting vram_setting __setting ( SETTING_MISC, vram ) = {
.name = "vram",
.description = "Video RAM",
.type = &setting_type_base64,
.scope = &builtin_scope,
};
/** Video RAM built-in setting */
struct builtin_setting vram_builtin_setting __builtin_setting = {
.setting = &vram_setting,
.fetch = vram_fetch,
};

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* 16550-compatible UART
*
*/
#include <errno.h>
#include <ipxe/uart.h>
/** UART port bases */
static uint16_t uart_base[] = {
[COM1] = 0x3f8,
[COM2] = 0x2f8,
[COM3] = 0x3e8,
[COM4] = 0x2e8,
};
/**
* Select UART port
*
* @v uart UART
* @v port Port number, or 0 to disable
* @ret rc Return status code
*/
int uart_select ( struct uart *uart, unsigned int port ) {
int rc;
/* Set new UART base */
if ( port >= ( sizeof ( uart_base ) / sizeof ( uart_base[0] ) ) ) {
rc = -ENODEV;
goto err;
}
uart->base = ( ( void * ) ( intptr_t ) uart_base[port] );
/* Check that UART exists */
if ( ( rc = uart_exists ( uart ) ) != 0 )
goto err;
return 0;
err:
uart->base = NULL;
return rc;
}

View File

@@ -0,0 +1,820 @@
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* Hyper-V driver
*
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
#include <pic8259.h>
#include <ipxe/malloc.h>
#include <ipxe/device.h>
#include <ipxe/timer.h>
#include <ipxe/quiesce.h>
#include <ipxe/cpuid.h>
#include <ipxe/msr.h>
#include <ipxe/hyperv.h>
#include <ipxe/vmbus.h>
#include "hyperv.h"
/** Maximum time to wait for a message response
*
* This is a policy decision.
*/
#define HV_MESSAGE_MAX_WAIT_MS 1000
/** Hyper-V timer frequency (fixed 10Mhz) */
#define HV_TIMER_HZ 10000000
/** Hyper-V timer scale factor (used to avoid 64-bit division) */
#define HV_TIMER_SHIFT 18
/**
* Convert a Hyper-V status code to an iPXE status code
*
* @v status Hyper-V status code
* @ret rc iPXE status code (before negation)
*/
#define EHV( status ) EPLATFORM ( EINFO_EPLATFORM, (status) )
/**
* Allocate zeroed pages
*
* @v hv Hyper-V hypervisor
* @v ... Page addresses to fill in, terminated by NULL
* @ret rc Return status code
*/
__attribute__ (( sentinel )) int
hv_alloc_pages ( struct hv_hypervisor *hv, ... ) {
va_list args;
void **page;
int i;
/* Allocate and zero pages */
va_start ( args, hv );
for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) {
*page = malloc_dma ( PAGE_SIZE, PAGE_SIZE );
if ( ! *page )
goto err_alloc;
memset ( *page, 0, PAGE_SIZE );
}
va_end ( args );
return 0;
err_alloc:
va_end ( args );
va_start ( args, hv );
for ( ; i >= 0 ; i-- ) {
page = va_arg ( args, void ** );
free_dma ( *page, PAGE_SIZE );
}
va_end ( args );
return -ENOMEM;
}
/**
* Free pages
*
* @v hv Hyper-V hypervisor
* @v ... Page addresses, terminated by NULL
*/
__attribute__ (( sentinel )) void
hv_free_pages ( struct hv_hypervisor *hv, ... ) {
va_list args;
void *page;
va_start ( args, hv );
while ( ( page = va_arg ( args, void * ) ) != NULL )
free_dma ( page, PAGE_SIZE );
va_end ( args );
}
/**
* Allocate message buffer
*
* @v hv Hyper-V hypervisor
* @ret rc Return status code
*/
static int hv_alloc_message ( struct hv_hypervisor *hv ) {
/* Allocate buffer. Must be aligned to at least 8 bytes and
* must not cross a page boundary, so align on its own size.
*/
hv->message = malloc_dma ( sizeof ( *hv->message ),
sizeof ( *hv->message ) );
if ( ! hv->message )
return -ENOMEM;
return 0;
}
/**
* Free message buffer
*
* @v hv Hyper-V hypervisor
*/
static void hv_free_message ( struct hv_hypervisor *hv ) {
/* Free buffer */
free_dma ( hv->message, sizeof ( *hv->message ) );
}
/**
* Check whether or not we are running in Hyper-V
*
* @ret rc Return status code
*/
static int hv_check_hv ( void ) {
struct x86_features features;
uint32_t interface_id;
uint32_t discard_ebx;
uint32_t discard_ecx;
uint32_t discard_edx;
/* Check for presence of a hypervisor (not necessarily Hyper-V) */
x86_features ( &features );
if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
DBGC ( HV_INTERFACE_ID, "HV not running in a hypervisor\n" );
return -ENODEV;
}
/* Check that hypervisor is Hyper-V */
cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx,
&discard_ecx, &discard_edx );
if ( interface_id != HV_INTERFACE_ID ) {
DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
"ID %#08x)\n", interface_id );
return -ENODEV;
}
return 0;
}
/**
* Check required features
*
* @v hv Hyper-V hypervisor
* @ret rc Return status code
*/
static int hv_check_features ( struct hv_hypervisor *hv ) {
uint32_t available;
uint32_t permissions;
uint32_t discard_ecx;
uint32_t discard_edx;
/* Check that required features and privileges are available */
cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx,
&discard_edx );
if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
hv, available, permissions );
return -ENODEV;
}
if ( ! ( available & HV_FEATURES_AVAIL_SYNIC_MSR ) ) {
DBGC ( hv, "HV %p has no SynIC MSRs (features %08x:%08x)\n",
hv, available, permissions );
return -ENODEV;
}
if ( ! ( permissions & HV_FEATURES_PERM_POST_MESSAGES ) ) {
DBGC ( hv, "HV %p cannot post messages (features %08x:%08x)\n",
hv, available, permissions );
return -EACCES;
}
if ( ! ( permissions & HV_FEATURES_PERM_SIGNAL_EVENTS ) ) {
DBGC ( hv, "HV %p cannot signal events (features %08x:%08x)",
hv, available, permissions );
return -EACCES;
}
return 0;
}
/**
* Check that Gen 2 UEFI firmware is not running
*
* @v hv Hyper-V hypervisor
* @ret rc Return status code
*
* We must not steal ownership from the Gen 2 UEFI firmware, since
* doing so will cause an immediate crash. Avoid this by checking for
* the guest OS identity known to be used by the Gen 2 UEFI firmware.
*/
static int hv_check_uefi ( struct hv_hypervisor *hv ) {
uint64_t guest_os_id;
/* Check for UEFI firmware's guest OS identity */
guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
if ( guest_os_id == HV_GUEST_OS_ID_UEFI ) {
DBGC ( hv, "HV %p is owned by UEFI firmware\n", hv );
return -ENOTSUP;
}
return 0;
}
/**
* Map hypercall page
*
* @v hv Hyper-V hypervisor
*/
static void hv_map_hypercall ( struct hv_hypervisor *hv ) {
union {
struct {
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
} __attribute__ (( packed ));
char text[ 13 /* "bbbbccccdddd" + NUL */ ];
} vendor_id;
uint32_t build;
uint32_t version;
uint32_t discard_eax;
uint32_t discard_ecx;
uint32_t discard_edx;
uint64_t guest_os_id;
uint64_t hypercall;
/* Report guest OS identity */
guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
if ( guest_os_id != 0 ) {
DBGC ( hv, "HV %p guest OS ID MSR was %#08llx\n",
hv, guest_os_id );
}
guest_os_id = HV_GUEST_OS_ID_IPXE;
DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
/* Get hypervisor system identity (for debugging) */
cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx,
&vendor_id.ecx, &vendor_id.edx );
vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx,
&discard_edx );
DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
/* Map hypercall page */
hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
hypercall &= ( PAGE_SIZE - 1 );
hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE );
DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
}
/**
* Unmap hypercall page
*
* @v hv Hyper-V hypervisor
*/
static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) {
uint64_t hypercall;
uint64_t guest_os_id;
/* Unmap the hypercall page */
hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE );
DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
/* Reset the guest OS identity */
guest_os_id = 0;
DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
}
/**
* Map synthetic interrupt controller
*
* @v hv Hyper-V hypervisor
*/
static void hv_map_synic ( struct hv_hypervisor *hv ) {
uint64_t simp;
uint64_t siefp;
uint64_t scontrol;
/* Zero SynIC message and event pages */
memset ( hv->synic.message, 0, PAGE_SIZE );
memset ( hv->synic.event, 0, PAGE_SIZE );
/* Map SynIC message page */
simp = rdmsr ( HV_X64_MSR_SIMP );
simp &= ( PAGE_SIZE - 1 );
simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE );
DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
wrmsr ( HV_X64_MSR_SIMP, simp );
/* Map SynIC event page */
siefp = rdmsr ( HV_X64_MSR_SIEFP );
siefp &= ( PAGE_SIZE - 1 );
siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE );
DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
wrmsr ( HV_X64_MSR_SIEFP, siefp );
/* Enable SynIC */
scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
scontrol |= HV_SCONTROL_ENABLE;
DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
}
/**
* Unmap synthetic interrupt controller, leaving SCONTROL untouched
*
* @v hv Hyper-V hypervisor
*/
static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor *hv ) {
uint64_t siefp;
uint64_t simp;
/* Unmap SynIC event page */
siefp = rdmsr ( HV_X64_MSR_SIEFP );
siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
wrmsr ( HV_X64_MSR_SIEFP, siefp );
/* Unmap SynIC message page */
simp = rdmsr ( HV_X64_MSR_SIMP );
simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE );
DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
wrmsr ( HV_X64_MSR_SIMP, simp );
}
/**
* Unmap synthetic interrupt controller
*
* @v hv Hyper-V hypervisor
*/
static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
uint64_t scontrol;
/* Disable SynIC */
scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
scontrol &= ~HV_SCONTROL_ENABLE;
DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
/* Unmap SynIC event and message pages */
hv_unmap_synic_no_scontrol ( hv );
}
/**
* Enable synthetic interrupt
*
* @v hv Hyper-V hypervisor
* @v sintx Synthetic interrupt number
*/
void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
unsigned long msr = HV_X64_MSR_SINT ( sintx );
uint64_t sint;
/* Enable synthetic interrupt
*
* We have to enable the interrupt, otherwise messages will
* not be delivered (even though the documentation implies
* that polling for messages is possible). We enable AutoEOI
* and hook the interrupt to the obsolete IRQ13 (FPU
* exception) vector, which will be implemented as a no-op.
*/
sint = rdmsr ( msr );
sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK );
sint |= ( HV_SINT_AUTO_EOI |
HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) );
DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
wrmsr ( msr, sint );
}
/**
* Disable synthetic interrupt
*
* @v hv Hyper-V hypervisor
* @v sintx Synthetic interrupt number
*/
void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
unsigned long msr = HV_X64_MSR_SINT ( sintx );
uint64_t sint;
/* Do nothing if interrupt is already disabled */
sint = rdmsr ( msr );
if ( sint & HV_SINT_MASKED )
return;
/* Disable synthetic interrupt */
sint &= ~HV_SINT_AUTO_EOI;
sint |= HV_SINT_MASKED;
DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
wrmsr ( msr, sint );
}
/**
* Post message
*
* @v hv Hyper-V hypervisor
* @v id Connection ID
* @v type Message type
* @v data Message
* @v len Length of message
* @ret rc Return status code
*/
int hv_post_message ( struct hv_hypervisor *hv, unsigned int id,
unsigned int type, const void *data, size_t len ) {
struct hv_post_message *msg = &hv->message->posted;
int status;
int rc;
/* Sanity check */
assert ( len <= sizeof ( msg->data ) );
/* Construct message */
memset ( msg, 0, sizeof ( *msg ) );
msg->id = cpu_to_le32 ( id );
msg->type = cpu_to_le32 ( type );
msg->len = cpu_to_le32 ( len );
memcpy ( msg->data, data, len );
DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n",
hv, id, type );
DBGC2_HDA ( hv, 0, msg->data, len );
/* Post message */
if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) {
rc = -EHV ( status );
DBGC ( hv, "HV %p could not post message to %#08x: %s\n",
hv, id, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Wait for received message
*
* @v hv Hyper-V hypervisor
* @v sintx Synthetic interrupt number
* @ret rc Return status code
*/
int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ) {
struct hv_message *msg = &hv->message->received;
struct hv_message *src = &hv->synic.message[sintx];
unsigned int retries;
size_t len;
/* Wait for message to arrive */
for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) {
/* Check for message */
if ( src->type ) {
/* Copy message */
memset ( msg, 0, sizeof ( *msg ) );
len = src->len;
assert ( len <= sizeof ( *msg ) );
memcpy ( msg, src,
( offsetof ( typeof ( *msg ), data ) + len ) );
DBGC2 ( hv, "HV %p SINT%d received message type "
"%#08x:\n", hv, sintx,
le32_to_cpu ( msg->type ) );
DBGC2_HDA ( hv, 0, msg->data, len );
/* Consume message */
src->type = 0;
return 0;
}
/* Trigger message delivery */
wrmsr ( HV_X64_MSR_EOM, 0 );
/* Delay */
mdelay ( 1 );
}
DBGC ( hv, "HV %p SINT%d timed out waiting for message\n",
hv, sintx );
return -ETIMEDOUT;
}
/**
* Signal event
*
* @v hv Hyper-V hypervisor
* @v id Connection ID
* @v flag Flag number
* @ret rc Return status code
*/
int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id,
unsigned int flag ) {
struct hv_signal_event *event = &hv->message->signalled;
int status;
int rc;
/* Construct event */
memset ( event, 0, sizeof ( *event ) );
event->id = cpu_to_le32 ( id );
event->flag = cpu_to_le16 ( flag );
/* Signal event */
if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) {
rc = -EHV ( status );
DBGC ( hv, "HV %p could not signal event to %#08x: %s\n",
hv, id, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Probe root device
*
* @v rootdev Root device
* @ret rc Return status code
*/
static int hv_probe ( struct root_device *rootdev ) {
struct hv_hypervisor *hv;
int rc;
/* Check we are running in Hyper-V */
if ( ( rc = hv_check_hv() ) != 0 )
goto err_check_hv;
/* Allocate and initialise structure */
hv = zalloc ( sizeof ( *hv ) );
if ( ! hv ) {
rc = -ENOMEM;
goto err_alloc;
}
/* Check features */
if ( ( rc = hv_check_features ( hv ) ) != 0 )
goto err_check_features;
/* Check that Gen 2 UEFI firmware is not running */
if ( ( rc = hv_check_uefi ( hv ) ) != 0 )
goto err_check_uefi;
/* Allocate pages */
if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
&hv->synic.event, NULL ) ) != 0 )
goto err_alloc_pages;
/* Allocate message buffer */
if ( ( rc = hv_alloc_message ( hv ) ) != 0 )
goto err_alloc_message;
/* Map hypercall page */
hv_map_hypercall ( hv );
/* Map synthetic interrupt controller */
hv_map_synic ( hv );
/* Probe Hyper-V devices */
if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 )
goto err_vmbus_probe;
rootdev_set_drvdata ( rootdev, hv );
return 0;
vmbus_remove ( hv, &rootdev->dev );
err_vmbus_probe:
hv_unmap_synic ( hv );
hv_unmap_hypercall ( hv );
hv_free_message ( hv );
err_alloc_message:
hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
NULL );
err_alloc_pages:
err_check_uefi:
err_check_features:
free ( hv );
err_alloc:
err_check_hv:
return rc;
}
/**
* Remove root device
*
* @v rootdev Root device
*/
static void hv_remove ( struct root_device *rootdev ) {
struct hv_hypervisor *hv = rootdev_get_drvdata ( rootdev );
vmbus_remove ( hv, &rootdev->dev );
hv_unmap_synic ( hv );
hv_unmap_hypercall ( hv );
hv_free_message ( hv );
hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
NULL );
free ( hv );
rootdev_set_drvdata ( rootdev, NULL );
}
/** Hyper-V root device driver */
static struct root_driver hv_root_driver = {
.probe = hv_probe,
.remove = hv_remove,
};
/** Hyper-V root device */
struct root_device hv_root_device __root_device = {
.dev = { .name = "Hyper-V" },
.driver = &hv_root_driver,
};
/**
* Quiesce system
*
*/
static void hv_quiesce ( void ) {
struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
unsigned int i;
/* Do nothing if we are not running in Hyper-V */
if ( ! hv )
return;
/* The "enlightened" portions of the Windows Server 2016 boot
* process will not cleanly take ownership of an active
* Hyper-V connection. Experimentation shows that the minimum
* requirement is that we disable the SynIC message page
* (i.e. zero the SIMP MSR).
*
* We cannot perform a full shutdown of the Hyper-V
* connection. Experimentation shows that if we disable the
* SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016
* will enter an indefinite wait loop.
*
* Attempt to create a safe handover environment by resetting
* all MSRs except for SCONTROL.
*
* Note that we do not shut down our VMBus devices, since we
* may need to unquiesce the system and continue operation.
*/
/* Disable all synthetic interrupts */
for ( i = 0 ; i <= HV_SINT_MAX ; i++ )
hv_disable_sint ( hv, i );
/* Unmap synthetic interrupt controller, leaving SCONTROL
* enabled (see above).
*/
hv_unmap_synic_no_scontrol ( hv );
/* Unmap hypercall page */
hv_unmap_hypercall ( hv );
DBGC ( hv, "HV %p quiesced\n", hv );
}
/**
* Unquiesce system
*
*/
static void hv_unquiesce ( void ) {
struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
uint64_t simp;
int rc;
/* Do nothing if we are not running in Hyper-V */
if ( ! hv )
return;
/* Experimentation shows that the "enlightened" portions of
* Windows Server 2016 will break our Hyper-V connection at
* some point during a SAN boot. Surprisingly it does not
* change the guest OS ID MSR, but it does leave the SynIC
* message page disabled.
*
* Our own explicit quiescing procedure will also disable the
* SynIC message page. We can therefore use the SynIC message
* page enable bit as a heuristic to determine when we need to
* reestablish our Hyper-V connection.
*/
simp = rdmsr ( HV_X64_MSR_SIMP );
if ( simp & HV_SIMP_ENABLE )
return;
/* Remap hypercall page */
hv_map_hypercall ( hv );
/* Remap synthetic interrupt controller */
hv_map_synic ( hv );
/* Reset Hyper-V devices */
if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) {
DBGC ( hv, "HV %p could not unquiesce: %s\n",
hv, strerror ( rc ) );
/* Nothing we can do */
return;
}
}
/** Hyper-V quiescer */
struct quiescer hv_quiescer __quiescer = {
.quiesce = hv_quiesce,
.unquiesce = hv_unquiesce,
};
/**
* Probe timer
*
* @ret rc Return status code
*/
static int hv_timer_probe ( void ) {
uint32_t available;
uint32_t discard_ebx;
uint32_t discard_ecx;
uint32_t discard_edx;
int rc;
/* Check we are running in Hyper-V */
if ( ( rc = hv_check_hv() ) != 0 )
return rc;
/* Check for available reference counter */
cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx,
&discard_edx );
if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );
return -ENODEV;
}
return 0;
}
/**
* Get current system time in ticks
*
* @ret ticks Current time, in ticks
*/
static unsigned long hv_currticks ( void ) {
/* Calculate time using a combination of bit shifts and
* multiplication (to avoid a 64-bit division).
*/
return ( ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) >> HV_TIMER_SHIFT ) *
( TICKS_PER_SEC / ( HV_TIMER_HZ >> HV_TIMER_SHIFT ) ) );
}
/**
* Delay for a fixed number of microseconds
*
* @v usecs Number of microseconds for which to delay
*/
static void hv_udelay ( unsigned long usecs ) {
uint32_t start;
uint32_t elapsed;
uint32_t threshold;
/* Spin until specified number of 10MHz ticks have elapsed */
start = rdmsr ( HV_X64_MSR_TIME_REF_COUNT );
threshold = ( usecs * ( HV_TIMER_HZ / 1000000 ) );
do {
elapsed = ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) - start );
} while ( elapsed < threshold );
}
/** Hyper-V timer */
struct timer hv_timer __timer ( TIMER_PREFERRED ) = {
.name = "Hyper-V",
.probe = hv_timer_probe,
.currticks = hv_currticks,
.udelay = hv_udelay,
};
/* Drag in objects via hv_root_device */
REQUIRING_SYMBOL ( hv_root_device );
/* Drag in netvsc driver */
REQUIRE_OBJECT ( netvsc );

View File

@@ -0,0 +1,63 @@
#ifndef _HYPERV_H
#define _HYPERV_H
/** @file
*
* Hyper-V driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Get vendor identification */
#define HV_CPUID_VENDOR_ID 0x40000000UL
/** Get interface identification */
#define HV_CPUID_INTERFACE_ID 0x40000001UL
/** Get hypervisor identification */
#define HV_CPUID_HYPERVISOR_ID 0x40000002UL
/** Get hypervisor features */
#define HV_CPUID_FEATURES 0x40000003UL
/** Time reference counter MSR is available */
#define HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR 0x00000002UL
/** SynIC MSRs are available */
#define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL
/** Hypercall MSRs are available */
#define HV_FEATURES_AVAIL_HYPERCALL_MSR 0x00000020UL
/** Guest may post messages */
#define HV_FEATURES_PERM_POST_MESSAGES 0x00000010UL
/** Guest may signal events */
#define HV_FEATURES_PERM_SIGNAL_EVENTS 0x00000020UL
/** Guest OS identity MSR */
#define HV_X64_MSR_GUEST_OS_ID 0x40000000UL
/** Hypercall page MSR */
#define HV_X64_MSR_HYPERCALL 0x40000001UL
/** Time reference MSR */
#define HV_X64_MSR_TIME_REF_COUNT 0x40000020UL
/** SynIC control MSR */
#define HV_X64_MSR_SCONTROL 0x40000080UL
/** SynIC event flags page MSR */
#define HV_X64_MSR_SIEFP 0x40000082UL
/** SynIC message page MSR */
#define HV_X64_MSR_SIMP 0x40000083UL
/** SynIC end of message MSR */
#define HV_X64_MSR_EOM 0x40000084UL
/** SynIC interrupt source MSRs */
#define HV_X64_MSR_SINT(x) ( 0x40000090UL + (x) )
#endif /* _HYPERV_H */

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ipxe/pci.h>
#include <undi.h>
#include <undirom.h>
#include <undiload.h>
#include <undinet.h>
#include <undipreload.h>
/** @file
*
* UNDI PCI driver
*
*/
/**
* Find UNDI ROM for PCI device
*
* @v pci PCI device
* @ret undirom UNDI ROM, or NULL
*
* Try to find a driver for this device. Try an exact match on the
* ROM address first, then fall back to a vendor/device ID match only
*/
static struct undi_rom * undipci_find_rom ( struct pci_device *pci ) {
struct undi_rom *undirom;
unsigned long rombase;
rombase = pci_bar_start ( pci, PCI_ROM_ADDRESS );
undirom = undirom_find_pci ( pci->vendor, pci->device, rombase );
if ( ! undirom )
undirom = undirom_find_pci ( pci->vendor, pci->device, 0 );
return undirom;
}
/**
* Probe PCI device
*
* @v pci PCI device
* @v id PCI ID
* @ret rc Return status code
*/
static int undipci_probe ( struct pci_device *pci ) {
struct undi_device *undi;
struct undi_rom *undirom;
int rc;
/* Allocate UNDI device structure */
undi = zalloc ( sizeof ( *undi ) );
if ( ! undi )
return -ENOMEM;
pci_set_drvdata ( pci, undi );
/* Find/create our pixie */
if ( preloaded_undi.pci_busdevfn == pci->busdevfn ) {
/* Claim preloaded UNDI device */
DBGC ( undi, "UNDI %p using preloaded UNDI device\n", undi );
memcpy ( undi, &preloaded_undi, sizeof ( *undi ) );
memset ( &preloaded_undi, 0, sizeof ( preloaded_undi ) );
} else {
/* Find UNDI ROM for PCI device */
if ( ! ( undirom = undipci_find_rom ( pci ) ) ) {
rc = -ENODEV;
goto err_find_rom;
}
/* Call UNDI ROM loader to create pixie */
if ( ( rc = undi_load_pci ( undi, undirom,
pci->busdevfn ) ) != 0 ) {
goto err_load_pci;
}
}
/* Create network device */
if ( ( rc = undinet_probe ( undi, &pci->dev ) ) != 0 )
goto err_undinet_probe;
return 0;
err_undinet_probe:
undi_unload ( undi );
err_find_rom:
err_load_pci:
free ( undi );
pci_set_drvdata ( pci, NULL );
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
static void undipci_remove ( struct pci_device *pci ) {
struct undi_device *undi = pci_get_drvdata ( pci );
undinet_remove ( undi );
undi_unload ( undi );
free ( undi );
pci_set_drvdata ( pci, NULL );
}
static struct pci_device_id undipci_nics[] = {
PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ),
};
struct pci_driver undipci_driver __pci_driver_fallback = {
.ids = undipci_nics,
.id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ),
.class = PCI_CLASS_ID ( PCI_CLASS_NETWORK, PCI_ANY_ID, PCI_ANY_ID ),
.probe = undipci_probe,
.remove = undipci_remove,
};

View File

@@ -0,0 +1,87 @@
FILE_LICENCE ( GPL2_OR_LATER )
#define PXENV_UNDI_ISR 0x0014
#define PXENV_UNDI_ISR_IN_START 1
#define PXENV_UNDI_ISR_OUT_OURS 0
#define PXENV_UNDI_ISR_OUT_NOT_OURS 1
#define IRQ_PIC_CUTOFF 8
#define ICR_EOI_NON_SPECIFIC 0x20
#define PIC1_ICR 0x20
#define PIC2_ICR 0xa0
.text
.arch i386
.code16
.section ".text16", "ax", @progbits
.globl undiisr
undiisr:
/* Preserve registers */
pushw %ds
pushw %es
pushw %fs
pushw %gs
pushfl
pushal
/* Set up our segment registers */
movw %cs:rm_ds, %ax
movw %ax, %ds
/* Check that we have an UNDI entry point */
cmpw $0, undinet_entry_point
je chain
/* Issue UNDI API call */
movw %ax, %es
movw $undinet_params, %di
movw $PXENV_UNDI_ISR, %bx
movw $PXENV_UNDI_ISR_IN_START, funcflag
pushw %es
pushw %di
pushw %bx
lcall *undinet_entry_point
cli /* Just in case */
addw $6, %sp
cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag
jne eoi
trig: /* Record interrupt occurence */
incb undiisr_trigger_count
eoi: /* Send EOI */
movb $ICR_EOI_NON_SPECIFIC, %al
cmpb $IRQ_PIC_CUTOFF, undiisr_irq
jb 1f
outb %al, $PIC2_ICR
1: outb %al, $PIC1_ICR
jmp exit
chain: /* Chain to next handler */
pushfw
lcall *undiisr_next_handler
exit: /* Restore registers and return */
cli
popal
movzwl %sp, %esp
addr32 movl -20(%esp), %esp /* %esp isn't restored by popal */
popfl
popw %gs
popw %fs
popw %es
popw %ds
iret
.section ".data16", "aw", @progbits
undinet_params:
status: .word 0
funcflag: .word 0
bufferlength: .word 0
framelength: .word 0
frameheaderlength: .word 0
frame: .word 0, 0
prottype: .byte 0
pkttype: .byte 0

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pxe.h>
#include <realmode.h>
#include <bios.h>
#include <pnpbios.h>
#include <basemem.h>
#include <ipxe/pci.h>
#include <undi.h>
#include <undirom.h>
#include <undiload.h>
/** @file
*
* UNDI load/unload
*
*/
/* Disambiguate the various error causes */
#define EINFO_EUNDILOAD \
__einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
"UNDI loader error" )
#define EUNDILOAD( status ) EPLATFORM ( EINFO_EUNDILOAD, status )
/** Parameter block for calling UNDI loader */
static struct s_UNDI_LOADER __bss16 ( undi_loader );
#define undi_loader __use_data16 ( undi_loader )
/** UNDI loader entry point */
static SEGOFF16_t __bss16 ( undi_loader_entry );
#define undi_loader_entry __use_data16 ( undi_loader_entry )
/**
* Call UNDI loader to create a pixie
*
* @v undi UNDI device
* @v undirom UNDI ROM
* @ret rc Return status code
*/
int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
struct s_PXE ppxe;
unsigned int fbms_seg;
uint16_t exit;
int rc;
/* Only one UNDI instance may be loaded at any given time */
if ( undi_loader_entry.segment ) {
DBG ( "UNDI %p cannot load multiple instances\n", undi );
rc = -EBUSY;
goto err_multiple;
}
/* Set up START_UNDI parameters */
memset ( &undi_loader, 0, sizeof ( undi_loader ) );
undi_loader.AX = undi->pci_busdevfn;
undi_loader.BX = undi->isapnp_csn;
undi_loader.DX = undi->isapnp_read_port;
undi_loader.ES = BIOS_SEG;
undi_loader.DI = find_pnp_bios();
/* Allocate base memory for PXE stack */
undi->restore_fbms = get_fbms();
fbms_seg = ( undi->restore_fbms << 6 );
fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 );
undi_loader.UNDI_CS = fbms_seg;
fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 );
undi_loader.UNDI_DS = fbms_seg;
undi->fbms = ( fbms_seg >> 6 );
set_fbms ( undi->fbms );
DBGC ( undi, "UNDI %p allocated [%d,%d) kB of base memory\n",
undi, undi->fbms, undi->restore_fbms );
/* Debug info */
DBGC ( undi, "UNDI %p loading ROM %p to CS %04x:%04zx DS %04x:%04zx "
"for ", undi, undirom, undi_loader.UNDI_CS, undirom->code_size,
undi_loader.UNDI_DS, undirom->data_size );
if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
unsigned int bus = ( undi->pci_busdevfn >> 8 );
unsigned int devfn = ( undi->pci_busdevfn & 0xff );
DBGC ( undi, "PCI %02x:%02x.%x\n",
bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) );
}
if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
DBGC ( undi, "ISAPnP(%04x) CSN %04x\n",
undi->isapnp_read_port, undi->isapnp_csn );
}
/* Call loader */
undi_loader_entry = undirom->loader_entry;
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
"pushw %%ds\n\t"
"pushw %%ax\n\t"
"lcall *undi_loader_entry\n\t"
"popl %%ebp\n\t" /* discard */
"popl %%ebp\n\t" /* gcc bug */ )
: "=a" ( exit )
: "a" ( __from_data16 ( &undi_loader ) )
: "ebx", "ecx", "edx", "esi", "edi" );
if ( exit != PXENV_EXIT_SUCCESS ) {
rc = -EUNDILOAD ( undi_loader.Status );
DBGC ( undi, "UNDI %p loader failed: %s\n",
undi, strerror ( rc ) );
goto err_loader;
}
/* Populate PXE device structure */
undi->pxenv = undi_loader.PXENVptr;
undi->ppxe = undi_loader.PXEptr;
copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset,
sizeof ( ppxe ) );
undi->entry = ppxe.EntryPointSP;
DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x "
"entry %04x:%04x\n", undi, undi->pxenv.segment,
undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset,
undi->entry.segment, undi->entry.offset );
return 0;
err_loader:
set_fbms ( undi->restore_fbms );
memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
err_multiple:
return rc;
}
/**
* Unload a pixie
*
* @v undi UNDI device
* @ret rc Return status code
*
* Erases the PXENV+ and !PXE signatures, and frees the used base
* memory (if possible).
*/
int undi_unload ( struct undi_device *undi ) {
static uint32_t dead = 0xdeaddead;
DBGC ( undi, "UNDI %p unloading\n", undi );
/* Clear entry point */
memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
/* Erase signatures */
if ( undi->pxenv.segment )
put_real ( dead, undi->pxenv.segment, undi->pxenv.offset );
if ( undi->ppxe.segment )
put_real ( dead, undi->ppxe.segment, undi->ppxe.offset );
/* Free base memory, if possible */
if ( undi->fbms == get_fbms() ) {
DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n",
undi, undi->fbms, undi->restore_fbms );
set_fbms ( undi->restore_fbms );
return 0;
} else {
DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n",
undi, undi->fbms, undi->restore_fbms );
return -EBUSY;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ipxe/device.h>
#include <ipxe/init.h>
#include <ipxe/pci.h>
#include <undi.h>
#include <undinet.h>
#include <undipreload.h>
/** @file
*
* "Pure" UNDI driver
*
* This is the UNDI driver without explicit support for PCI or any
* other bus type. It is capable only of using the preloaded UNDI
* device. It must not be combined in an image with any other
* drivers.
*
* If you want a PXE-loadable image that contains only the UNDI
* driver, build "bin/undionly.kpxe".
*
* If you want any other image format, or any other drivers in
* addition to the UNDI driver, build e.g. "bin/undi.dsk".
*/
/** UNDI root bus device */
static struct device undibus_dev;
/**
* Probe UNDI root bus
*
* @v rootdev UNDI bus root device
*
* Scans the UNDI bus for devices and registers all devices it can
* find.
*/
static int undibus_probe ( struct root_device *rootdev ) {
struct undi_device *undi = &preloaded_undi;
struct device *dev = &undibus_dev;
int rc;
/* Check for a valie preloaded UNDI device */
if ( ! undi->entry.segment ) {
DBG ( "No preloaded UNDI device found!\n" );
return -ENODEV;
}
/* Add to device hierarchy */
dev->driver_name = "undionly";
if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) {
dev->desc.bus_type = BUS_TYPE_PCI;
dev->desc.location = undi->pci_busdevfn;
dev->desc.vendor = undi->pci_vendor;
dev->desc.device = undi->pci_device;
snprintf ( dev->name, sizeof ( dev->name ),
"0000:%02x:%02x.%x", PCI_BUS ( undi->pci_busdevfn ),
PCI_SLOT ( undi->pci_busdevfn ),
PCI_FUNC ( undi->pci_busdevfn ) );
} else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) {
dev->desc.bus_type = BUS_TYPE_ISAPNP;
snprintf ( dev->name, sizeof ( dev->name ), "ISAPNP" );
}
dev->parent = &rootdev->dev;
list_add ( &dev->siblings, &rootdev->dev.children);
INIT_LIST_HEAD ( &dev->children );
/* Create network device */
if ( ( rc = undinet_probe ( undi, dev ) ) != 0 )
goto err;
return 0;
err:
list_del ( &dev->siblings );
return rc;
}
/**
* Remove UNDI root bus
*
* @v rootdev UNDI bus root device
*/
static void undibus_remove ( struct root_device *rootdev __unused ) {
struct undi_device *undi = &preloaded_undi;
struct device *dev = &undibus_dev;
undinet_remove ( undi );
list_del ( &dev->siblings );
}
/** UNDI bus root device driver */
static struct root_driver undi_root_driver = {
.probe = undibus_probe,
.remove = undibus_remove,
};
/** UNDI bus root device */
struct root_device undi_root_device __root_device = {
.dev = { .name = "UNDI" },
.driver = &undi_root_driver,
};
/**
* Prepare for exit
*
* @v booting System is shutting down for OS boot
*/
static void undionly_shutdown ( int booting ) {
/* If we are shutting down to boot an OS, clear the "keep PXE
* stack" flag.
*/
if ( booting )
preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL;
}
struct startup_fn startup_undionly __startup_fn ( STARTUP_LATE ) = {
.name = "undionly",
.shutdown = undionly_shutdown,
};

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <realmode.h>
#include <undipreload.h>
/** @file
*
* Preloaded UNDI stack
*
*/
/**
* Preloaded UNDI device
*
* This is the UNDI device that was present when Etherboot started
* execution (i.e. when loading a .kpxe image). The first driver to
* claim this device must zero out this data structure.
*/
struct undi_device __data16 ( preloaded_undi );

View File

@@ -0,0 +1,235 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pxe.h>
#include <realmode.h>
#include <undirom.h>
/** @file
*
* UNDI expansion ROMs
*
*/
/** List of all UNDI ROMs */
static LIST_HEAD ( undiroms );
/**
* Parse PXE ROM ID structure
*
* @v undirom UNDI ROM
* @v pxeromid Offset within ROM to PXE ROM ID structure
* @ret rc Return status code
*/
static int undirom_parse_pxeromid ( struct undi_rom *undirom,
unsigned int pxeromid ) {
struct undi_rom_id undi_rom_id;
unsigned int undiloader;
DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom,
undirom->rom_segment, pxeromid );
/* Read PXE ROM ID structure and verify */
copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid,
sizeof ( undi_rom_id ) );
if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature "
"%08x\n", undirom, undi_rom_id.Signature );
return -EINVAL;
}
/* Check for UNDI loader */
undiloader = undi_rom_id.UNDILoader;
if ( ! undiloader ) {
DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom );
return -EINVAL;
}
/* Fill in UNDI ROM loader fields */
undirom->loader_entry.segment = undirom->rom_segment;
undirom->loader_entry.offset = undiloader;
undirom->code_size = undi_rom_id.CodeSize;
undirom->data_size = undi_rom_id.DataSize;
DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x "
"(code %04zx data %04zx)\n", undirom,
undirom->loader_entry.segment, undirom->loader_entry.offset,
undirom->code_size, undirom->data_size );
return 0;
}
/**
* Parse PCI expansion header
*
* @v undirom UNDI ROM
* @v pcirheader Offset within ROM to PCI expansion header
*/
static int undirom_parse_pcirheader ( struct undi_rom *undirom,
unsigned int pcirheader ) {
struct pcir_header pcir_header;
DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n",
undirom, undirom->rom_segment, pcirheader );
/* Read PCI expansion header and verify */
copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader,
sizeof ( pcir_header ) );
if ( pcir_header.signature != PCIR_SIGNATURE ) {
DBGC ( undirom, "UNDIROM %p has bad PCI expansion header "
"signature %08x\n", undirom, pcir_header.signature );
return -EINVAL;
}
/* Fill in UNDI ROM PCI device fields */
undirom->bus_type = PCI_NIC;
undirom->bus_id.pci.vendor_id = pcir_header.vendor_id;
undirom->bus_id.pci.device_id = pcir_header.device_id;
DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom,
undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id );
return 0;
}
/**
* Probe UNDI ROM
*
* @v rom_segment ROM segment address
* @ret rc Return status code
*/
static int undirom_probe ( unsigned int rom_segment ) {
struct undi_rom *undirom = NULL;
struct undi_rom_header romheader;
size_t rom_len;
unsigned int pxeromid;
unsigned int pcirheader;
int rc;
/* Read expansion ROM header and verify */
copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) );
if ( romheader.Signature != ROM_SIGNATURE ) {
rc = -EINVAL;
goto err;
}
rom_len = ( romheader.ROMLength * 512 );
/* Allocate memory for UNDI ROM */
undirom = zalloc ( sizeof ( *undirom ) );
if ( ! undirom ) {
DBG ( "Could not allocate UNDI ROM structure\n" );
rc = -ENOMEM;
goto err;
}
DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 "
"(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) );
undirom->rom_segment = rom_segment;
/* Check for and parse PXE ROM ID */
pxeromid = romheader.PXEROMID;
if ( ! pxeromid ) {
DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom );
rc = -EINVAL;
goto err;
}
if ( pxeromid > rom_len ) {
DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n",
undirom );
rc = -EINVAL;
goto err;
}
if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 )
goto err;
/* Parse PCIR header, if present */
pcirheader = romheader.PCIRHeader;
if ( pcirheader )
undirom_parse_pcirheader ( undirom, pcirheader );
/* Add to UNDI ROM list and return */
DBGC ( undirom, "UNDIROM %p registered\n", undirom );
list_add_tail ( &undirom->list, &undiroms );
return 0;
err:
free ( undirom );
return rc;
}
/**
* Create UNDI ROMs for all possible expansion ROMs
*
* @ret
*/
static void undirom_probe_all_roms ( void ) {
static int probed = 0;
unsigned int rom_segment;
/* Perform probe only once */
if ( probed )
return;
DBG ( "Scanning for PXE expansion ROMs\n" );
/* Scan through expansion ROM region at 512 byte intervals */
for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
rom_segment += 0x20 ) {
undirom_probe ( rom_segment );
}
probed = 1;
}
/**
* Find UNDI ROM for PCI device
*
* @v vendor_id PCI vendor ID
* @v device_id PCI device ID
* @v rombase ROM base address, or 0 for any
* @ret undirom UNDI ROM, or NULL
*/
struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
unsigned int device_id,
unsigned int rombase ) {
struct undi_rom *undirom;
undirom_probe_all_roms();
list_for_each_entry ( undirom, &undiroms, list ) {
if ( undirom->bus_type != PCI_NIC )
continue;
if ( undirom->bus_id.pci.vendor_id != vendor_id )
continue;
if ( undirom->bus_id.pci.device_id != device_id )
continue;
if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) )
continue;
DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n",
undirom, vendor_id, device_id, rombase );
return undirom;
}
DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n",
vendor_id, device_id, rombase );
return NULL;
}

View File

@@ -0,0 +1,505 @@
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <ipxe/malloc.h>
#include <ipxe/pci.h>
#include <ipxe/cpuid.h>
#include <ipxe/msr.h>
#include <ipxe/xen.h>
#include <ipxe/xenver.h>
#include <ipxe/xenmem.h>
#include <ipxe/xenstore.h>
#include <ipxe/xenbus.h>
#include <ipxe/xengrant.h>
#include "hvm.h"
/** @file
*
* Xen HVM driver
*
*/
/**
* Get CPUID base
*
* @v hvm HVM device
* @ret rc Return status code
*/
static int hvm_cpuid_base ( struct hvm_device *hvm ) {
struct {
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
} __attribute__ (( packed )) signature;
uint32_t base;
uint32_t version;
uint32_t discard_eax;
uint32_t discard_ebx;
uint32_t discard_ecx;
uint32_t discard_edx;
/* Scan for magic signature */
for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
base += HVM_CPUID_STEP ) {
cpuid ( base, 0, &discard_eax, &signature.ebx, &signature.ecx,
&signature.edx );
if ( memcmp ( &signature, HVM_CPUID_MAGIC,
sizeof ( signature ) ) == 0 ) {
hvm->cpuid_base = base;
cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version,
&discard_ebx, &discard_ecx, &discard_edx );
DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
base, ( version >> 16 ), ( version & 0xffff ) );
return 0;
}
}
DBGC ( hvm, "HVM could not find hypervisor\n" );
return -ENODEV;
}
/**
* Map hypercall page(s)
*
* @v hvm HVM device
* @ret rc Return status code
*/
static int hvm_map_hypercall ( struct hvm_device *hvm ) {
uint32_t pages;
uint32_t msr;
uint32_t discard_ecx;
uint32_t discard_edx;
physaddr_t hypercall_phys;
uint32_t version;
static xen_extraversion_t extraversion;
int xenrc;
int rc;
/* Get number of hypercall pages and MSR to use */
cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), 0, &pages, &msr,
&discard_ecx, &discard_edx );
/* Allocate pages */
hvm->hypercall_len = ( pages * PAGE_SIZE );
hvm->xen.hypercall = malloc_dma ( hvm->hypercall_len, PAGE_SIZE );
if ( ! hvm->xen.hypercall ) {
DBGC ( hvm, "HVM could not allocate %d hypercall page(s)\n",
pages );
return -ENOMEM;
}
hypercall_phys = virt_to_phys ( hvm->xen.hypercall );
DBGC2 ( hvm, "HVM hypercall page(s) at [%#08lx,%#08lx) via MSR %#08x\n",
hypercall_phys, ( hypercall_phys + hvm->hypercall_len ), msr );
/* Write to MSR */
wrmsr ( msr, hypercall_phys );
/* Check that hypercall mechanism is working */
version = xenver_version ( &hvm->xen );
if ( ( xenrc = xenver_extraversion ( &hvm->xen, &extraversion ) ) != 0){
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not get extraversion: %s\n",
strerror ( rc ) );
return rc;
}
DBGC2 ( hvm, "HVM found Xen version %d.%d%s\n",
( version >> 16 ), ( version & 0xffff ) , extraversion );
return 0;
}
/**
* Unmap hypercall page(s)
*
* @v hvm HVM device
*/
static void hvm_unmap_hypercall ( struct hvm_device *hvm ) {
/* Free pages */
free_dma ( hvm->xen.hypercall, hvm->hypercall_len );
}
/**
* Allocate and map MMIO space
*
* @v hvm HVM device
* @v space Source mapping space
* @v len Length (must be a multiple of PAGE_SIZE)
* @ret mmio MMIO space address, or NULL on error
*/
static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space,
size_t len ) {
struct xen_add_to_physmap add;
struct xen_remove_from_physmap remove;
unsigned int pages = ( len / PAGE_SIZE );
physaddr_t mmio_phys;
unsigned int i;
void *mmio;
int xenrc;
int rc;
/* Sanity check */
assert ( ( len % PAGE_SIZE ) == 0 );
/* Check for available space */
if ( ( hvm->mmio_offset + len ) > hvm->mmio_len ) {
DBGC ( hvm, "HVM could not allocate %zd bytes of MMIO space "
"(%zd of %zd remaining)\n", len,
( hvm->mmio_len - hvm->mmio_offset ), hvm->mmio_len );
goto err_no_space;
}
/* Map this space */
mmio = pci_ioremap ( hvm->pci, ( hvm->mmio + hvm->mmio_offset ), len );
if ( ! mmio ) {
DBGC ( hvm, "HVM could not map MMIO space [%08lx,%08lx)\n",
( hvm->mmio + hvm->mmio_offset ),
( hvm->mmio + hvm->mmio_offset + len ) );
goto err_ioremap;
}
mmio_phys = virt_to_phys ( mmio );
/* Add to physical address space */
for ( i = 0 ; i < pages ; i++ ) {
add.domid = DOMID_SELF;
add.idx = i;
add.space = space;
add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
if ( ( xenrc = xenmem_add_to_physmap ( &hvm->xen, &add ) ) !=0){
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not add space %d idx %d at "
"[%08lx,%08lx): %s\n", space, i,
( mmio_phys + ( i * PAGE_SIZE ) ),
( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
strerror ( rc ) );
goto err_add_to_physmap;
}
}
/* Update offset */
hvm->mmio_offset += len;
return mmio;
i = pages;
err_add_to_physmap:
for ( i-- ; ( signed int ) i >= 0 ; i-- ) {
remove.domid = DOMID_SELF;
add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
xenmem_remove_from_physmap ( &hvm->xen, &remove );
}
iounmap ( mmio );
err_ioremap:
err_no_space:
return NULL;
}
/**
* Unmap MMIO space
*
* @v hvm HVM device
* @v mmio MMIO space address
* @v len Length (must be a multiple of PAGE_SIZE)
*/
static void hvm_iounmap ( struct hvm_device *hvm, void *mmio, size_t len ) {
struct xen_remove_from_physmap remove;
physaddr_t mmio_phys = virt_to_phys ( mmio );
unsigned int pages = ( len / PAGE_SIZE );
unsigned int i;
int xenrc;
int rc;
/* Unmap this space */
iounmap ( mmio );
/* Remove from physical address space */
for ( i = 0 ; i < pages ; i++ ) {
remove.domid = DOMID_SELF;
remove.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
if ( ( xenrc = xenmem_remove_from_physmap ( &hvm->xen,
&remove ) ) != 0 ) {
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not remove space [%08lx,%08lx): "
"%s\n", ( mmio_phys + ( i * PAGE_SIZE ) ),
( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
strerror ( rc ) );
/* Nothing we can do about this */
}
}
}
/**
* Map shared info page
*
* @v hvm HVM device
* @ret rc Return status code
*/
static int hvm_map_shared_info ( struct hvm_device *hvm ) {
physaddr_t shared_info_phys;
int rc;
/* Map shared info page */
hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info,
PAGE_SIZE );
if ( ! hvm->xen.shared ) {
rc = -ENOMEM;
goto err_alloc;
}
shared_info_phys = virt_to_phys ( hvm->xen.shared );
DBGC2 ( hvm, "HVM shared info page at [%#08lx,%#08lx)\n",
shared_info_phys, ( shared_info_phys + PAGE_SIZE ) );
/* Sanity check */
DBGC2 ( hvm, "HVM wallclock time is %d\n",
readl ( &hvm->xen.shared->wc_sec ) );
return 0;
hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE );
err_alloc:
return rc;
}
/**
* Unmap shared info page
*
* @v hvm HVM device
*/
static void hvm_unmap_shared_info ( struct hvm_device *hvm ) {
/* Unmap shared info page */
hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE );
}
/**
* Map grant table
*
* @v hvm HVM device
* @ret rc Return status code
*/
static int hvm_map_grant ( struct hvm_device *hvm ) {
physaddr_t grant_phys;
int rc;
/* Initialise grant table */
if ( ( rc = xengrant_init ( &hvm->xen ) ) != 0 ) {
DBGC ( hvm, "HVM could not initialise grant table: %s\n",
strerror ( rc ) );
return rc;
}
/* Map grant table */
hvm->xen.grant.table = hvm_ioremap ( hvm, XENMAPSPACE_grant_table,
hvm->xen.grant.len );
if ( ! hvm->xen.grant.table )
return -ENODEV;
grant_phys = virt_to_phys ( hvm->xen.grant.table );
DBGC2 ( hvm, "HVM mapped grant table at [%08lx,%08lx)\n",
grant_phys, ( grant_phys + hvm->xen.grant.len ) );
return 0;
}
/**
* Unmap grant table
*
* @v hvm HVM device
*/
static void hvm_unmap_grant ( struct hvm_device *hvm ) {
/* Unmap grant table */
hvm_iounmap ( hvm, hvm->xen.grant.table, hvm->xen.grant.len );
}
/**
* Map XenStore
*
* @v hvm HVM device
* @ret rc Return status code
*/
static int hvm_map_xenstore ( struct hvm_device *hvm ) {
uint64_t xenstore_evtchn;
uint64_t xenstore_pfn;
physaddr_t xenstore_phys;
char *name;
int xenrc;
int rc;
/* Get XenStore event channel */
if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_EVTCHN,
&xenstore_evtchn ) ) != 0 ) {
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not get XenStore event channel: %s\n",
strerror ( rc ) );
return rc;
}
hvm->xen.store.port = xenstore_evtchn;
/* Get XenStore PFN */
if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_PFN,
&xenstore_pfn ) ) != 0 ) {
rc = -EXEN ( xenrc );
DBGC ( hvm, "HVM could not get XenStore PFN: %s\n",
strerror ( rc ) );
return rc;
}
xenstore_phys = ( xenstore_pfn * PAGE_SIZE );
/* Map XenStore */
hvm->xen.store.intf = pci_ioremap ( hvm->pci, xenstore_phys,
PAGE_SIZE );
if ( ! hvm->xen.store.intf ) {
DBGC ( hvm, "HVM could not map XenStore at [%08lx,%08lx)\n",
xenstore_phys, ( xenstore_phys + PAGE_SIZE ) );
return -ENODEV;
}
DBGC2 ( hvm, "HVM mapped XenStore at [%08lx,%08lx) with event port "
"%d\n", xenstore_phys, ( xenstore_phys + PAGE_SIZE ),
hvm->xen.store.port );
/* Check that XenStore is working */
if ( ( rc = xenstore_read ( &hvm->xen, &name, "name", NULL ) ) != 0 ) {
DBGC ( hvm, "HVM could not read domain name: %s\n",
strerror ( rc ) );
return rc;
}
DBGC2 ( hvm, "HVM running in domain \"%s\"\n", name );
free ( name );
return 0;
}
/**
* Unmap XenStore
*
* @v hvm HVM device
*/
static void hvm_unmap_xenstore ( struct hvm_device *hvm ) {
/* Unmap XenStore */
iounmap ( hvm->xen.store.intf );
}
/**
* Probe PCI device
*
* @v pci PCI device
* @ret rc Return status code
*/
static int hvm_probe ( struct pci_device *pci ) {
struct hvm_device *hvm;
int rc;
/* Allocate and initialise structure */
hvm = zalloc ( sizeof ( *hvm ) );
if ( ! hvm ) {
rc = -ENOMEM;
goto err_alloc;
}
hvm->pci = pci;
hvm->mmio = pci_bar_start ( pci, HVM_MMIO_BAR );
hvm->mmio_len = pci_bar_size ( pci, HVM_MMIO_BAR );
DBGC2 ( hvm, "HVM has MMIO space [%08lx,%08lx)\n",
hvm->mmio, ( hvm->mmio + hvm->mmio_len ) );
/* Fix up PCI device */
adjust_pci_device ( pci );
/* Attach to hypervisor */
if ( ( rc = hvm_cpuid_base ( hvm ) ) != 0 )
goto err_cpuid_base;
if ( ( rc = hvm_map_hypercall ( hvm ) ) != 0 )
goto err_map_hypercall;
if ( ( rc = hvm_map_shared_info ( hvm ) ) != 0 )
goto err_map_shared_info;
if ( ( rc = hvm_map_grant ( hvm ) ) != 0 )
goto err_map_grant;
if ( ( rc = hvm_map_xenstore ( hvm ) ) != 0 )
goto err_map_xenstore;
/* Probe Xen devices */
if ( ( rc = xenbus_probe ( &hvm->xen, &pci->dev ) ) != 0 ) {
DBGC ( hvm, "HVM could not probe Xen bus: %s\n",
strerror ( rc ) );
goto err_xenbus_probe;
}
pci_set_drvdata ( pci, hvm );
return 0;
xenbus_remove ( &hvm->xen, &pci->dev );
err_xenbus_probe:
hvm_unmap_xenstore ( hvm );
err_map_xenstore:
hvm_unmap_grant ( hvm );
err_map_grant:
hvm_unmap_shared_info ( hvm );
err_map_shared_info:
hvm_unmap_hypercall ( hvm );
err_map_hypercall:
err_cpuid_base:
free ( hvm );
err_alloc:
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
static void hvm_remove ( struct pci_device *pci ) {
struct hvm_device *hvm = pci_get_drvdata ( pci );
xenbus_remove ( &hvm->xen, &pci->dev );
hvm_unmap_xenstore ( hvm );
hvm_unmap_grant ( hvm );
hvm_unmap_shared_info ( hvm );
hvm_unmap_hypercall ( hvm );
free ( hvm );
}
/** PCI device IDs */
static struct pci_device_id hvm_ids[] = {
PCI_ROM ( 0x5853, 0x0001, "hvm", "hvm", 0 ),
PCI_ROM ( 0x5853, 0x0002, "hvm2", "hvm2", 0 ),
};
/** PCI driver */
struct pci_driver hvm_driver __pci_driver = {
.ids = hvm_ids,
.id_count = ( sizeof ( hvm_ids ) / sizeof ( hvm_ids[0] ) ),
.probe = hvm_probe,
.remove = hvm_remove,
};
/* Drag in objects via hvm_driver */
REQUIRING_SYMBOL ( hvm_driver );
/* Drag in netfront driver */
REQUIRE_OBJECT ( netfront );

View File

@@ -0,0 +1,77 @@
#ifndef _HVM_H
#define _HVM_H
/** @file
*
* Xen HVM driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/xen.h>
#include <xen/hvm/hvm_op.h>
#include <xen/hvm/params.h>
/** Minimum CPUID base */
#define HVM_CPUID_MIN 0x40000000UL
/** Maximum CPUID base */
#define HVM_CPUID_MAX 0x4000ff00UL
/** Increment between CPUID bases */
#define HVM_CPUID_STEP 0x00000100UL
/** Magic signature */
#define HVM_CPUID_MAGIC "XenVMMXenVMM"
/** Get Xen version */
#define HVM_CPUID_VERSION 1
/** Get number of hypercall pages */
#define HVM_CPUID_PAGES 2
/** PCI MMIO BAR */
#define HVM_MMIO_BAR PCI_BASE_ADDRESS_1
/** A Xen HVM device */
struct hvm_device {
/** Xen hypervisor */
struct xen_hypervisor xen;
/** PCI device */
struct pci_device *pci;
/** CPUID base */
uint32_t cpuid_base;
/** Length of hypercall table */
size_t hypercall_len;
/** MMIO base address */
unsigned long mmio;
/** Current offset within MMIO address space */
size_t mmio_offset;
/** Length of MMIO address space */
size_t mmio_len;
};
/**
* Get HVM parameter value
*
* @v xen Xen hypervisor
* @v index Parameter index
* @v value Value to fill in
* @ret xenrc Xen status code
*/
static inline int xen_hvm_get_param ( struct xen_hypervisor *xen,
unsigned int index, uint64_t *value ) {
struct xen_hvm_param param;
int xenrc;
param.domid = DOMID_SELF;
param.index = index;
xenrc = xen_hypercall_2 ( xen, __HYPERVISOR_hvm_op, HVMOP_get_param,
virt_to_phys ( &param ) );
*value = param.value;
return xenrc;
}
#endif /* _HVM_H */

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
#include <ipxe/netdevice.h>
#include <ipxe/command.h>
#include <ipxe/parseopt.h>
#include <hci/ifmgmt_cmd.h>
#include <pxe_call.h>
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* PXE commands
*
*/
/** "startpxe" options */
struct startpxe_options {};
/** "startpxe" option list */
static struct option_descriptor startpxe_opts[] = {};
/**
* "startpxe" payload
*
* @v netdev Network device
* @v opts Command options
* @ret rc Return status code
*/
static int startpxe_payload ( struct net_device *netdev,
struct startpxe_options *opts __unused ) {
if ( netdev_is_open ( netdev ) )
pxe_activate ( netdev );
return 0;
}
/** "startpxe" command descriptor */
static struct ifcommon_command_descriptor startpxe_cmd =
IFCOMMON_COMMAND_DESC ( struct startpxe_options, startpxe_opts,
0, MAX_ARGUMENTS, "[<interface>]",
startpxe_payload, 0 );
/**
* The "startpxe" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int startpxe_exec ( int argc, char **argv ) {
return ifcommon_exec ( argc, argv, &startpxe_cmd );
}
/** "stoppxe" options */
struct stoppxe_options {};
/** "stoppxe" option list */
static struct option_descriptor stoppxe_opts[] = {};
/** "stoppxe" command descriptor */
static struct command_descriptor stoppxe_cmd =
COMMAND_DESC ( struct stoppxe_options, stoppxe_opts, 0, 0, NULL );
/**
* The "stoppxe" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int stoppxe_exec ( int argc __unused, char **argv __unused ) {
struct stoppxe_options opts;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, &stoppxe_cmd, &opts ) ) != 0 )
return rc;
pxe_deactivate();
return 0;
}
/** PXE commands */
struct command pxe_commands[] __command = {
{
.name = "startpxe",
.exec = startpxe_exec,
},
{
.name = "stoppxe",
.exec = stoppxe_exec,
},
};

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/**
* @file
*
* x86 bootsector image format
*
*/
#include <errno.h>
#include <realmode.h>
#include <biosint.h>
#include <bootsector.h>
#include <ipxe/console.h>
/** Vector for storing original INT 18 handler
*
* We do not chain to this vector, so there is no need to place it in
* .text16.
*/
static struct segoff int18_vector;
/** Vector for storing original INT 19 handler
*
* We do not chain to this vector, so there is no need to place it in
* .text16.
*/
static struct segoff int19_vector;
/** Restart point for INT 18 or 19 */
extern void bootsector_exec_fail ( void );
/**
* Jump to preloaded bootsector
*
* @v segment Real-mode segment
* @v offset Real-mode offset
* @v drive Drive number to pass to boot sector
* @ret rc Return status code
*/
int call_bootsector ( unsigned int segment, unsigned int offset,
unsigned int drive ) {
int discard_b, discard_D, discard_d;
/* Reset console, since boot sector will probably use it */
console_reset();
DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset );
/* Hook INTs 18 and 19 to capture failure paths */
hook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail,
&int18_vector );
hook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail,
&int19_vector );
/* Boot the loaded sector
*
* We assume that the boot sector may completely destroy our
* real-mode stack, so we preserve everything we need in
* static storage.
*/
__asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */
"popw %%cs:saved_retaddr\n\t"
/* Save stack pointer */
"movw %%ss, %%ax\n\t"
"movw %%ax, %%cs:saved_ss\n\t"
"movw %%sp, %%cs:saved_sp\n\t"
/* Save frame pointer (gcc bug) */
"movl %%ebp, %%cs:saved_ebp\n\t"
/* Prepare jump to boot sector */
"pushw %%bx\n\t"
"pushw %%di\n\t"
/* Clear all registers */
"xorl %%eax, %%eax\n\t"
"xorl %%ebx, %%ebx\n\t"
"xorl %%ecx, %%ecx\n\t"
/* %edx contains drive number */
"xorl %%esi, %%esi\n\t"
"xorl %%edi, %%edi\n\t"
"xorl %%ebp, %%ebp\n\t"
"movw %%ax, %%ds\n\t"
"movw %%ax, %%es\n\t"
"movw %%ax, %%fs\n\t"
"movw %%ax, %%gs\n\t"
/* Jump to boot sector */
"sti\n\t"
"lret\n\t"
/* Preserved variables */
"\nsaved_ebp: .long 0\n\t"
"\nsaved_ss: .word 0\n\t"
"\nsaved_sp: .word 0\n\t"
"\nsaved_retaddr: .word 0\n\t"
/* Boot failure return point */
"\nbootsector_exec_fail:\n\t"
/* Restore frame pointer (gcc bug) */
"movl %%cs:saved_ebp, %%ebp\n\t"
/* Restore stack pointer */
"movw %%cs:saved_ss, %%ax\n\t"
"movw %%ax, %%ss\n\t"
"movw %%cs:saved_sp, %%sp\n\t"
/* Return via saved address */
"jmp *%%cs:saved_retaddr\n\t" )
: "=b" ( discard_b ), "=D" ( discard_D ),
"=d" ( discard_d )
: "b" ( segment ), "D" ( offset ),
"d" ( drive )
: "eax", "ecx", "esi" );
DBG ( "Booted disk returned via INT 18 or 19\n" );
/* Unhook INTs 18 and 19 */
unhook_bios_interrupt ( 0x18, ( intptr_t ) bootsector_exec_fail,
&int18_vector );
unhook_bios_interrupt ( 0x19, ( intptr_t ) bootsector_exec_fail,
&int19_vector );
return -ECANCELED;
}

Some files were not shown because too many files have changed in this diff Show More