mirror of
https://github.com/isfshax/isfshax.git
synced 2024-11-06 03:35:05 +01:00
first commit
This commit is contained in:
commit
ba607fb319
339
COPYING
Normal file
339
COPYING
Normal 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.
|
151
Makefile
Normal file
151
Makefile
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITARM)),)
|
||||||
|
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(DEVKITARM)/ds_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# SPECS is the directory containing the important build and link files
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export TARGET := stage2
|
||||||
|
export BUILD ?= build
|
||||||
|
|
||||||
|
R_SOURCES :=
|
||||||
|
SOURCES := stage2 stage2/fatfs stage2/isfs
|
||||||
|
|
||||||
|
R_INCLUDES :=
|
||||||
|
INCLUDES := stage2
|
||||||
|
|
||||||
|
DATA :=
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv5te -mcpu=arm926ej-s -marm -mthumb-interwork -mbig-endian -mfloat-abi=soft
|
||||||
|
|
||||||
|
CFLAGS := -g -std=c11 -Wall -Wno-unused-function -O3 \
|
||||||
|
-fomit-frame-pointer -ffunction-sections \
|
||||||
|
$(ARCH)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -DCAN_HAZ_IRQ -D_GNU_SOURCE -fno-builtin-printf -Wno-nonnull
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -n -nostartfiles -g --specs=../stage2/link.specs $(ARCH) -Wl,--gc-sections,-Map,$(TARGET).map \
|
||||||
|
-L$(DEVKITARM)/lib/gcc/arm-none-eabi/5.3.0/be -L$(DEVKITARM)/arm-none-eabi/lib/be
|
||||||
|
|
||||||
|
LIBS :=
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS :=
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export ROOTDIR := $(CURDIR)
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
|
||||||
|
SOURCES := $(SOURCES) $(foreach dir,$(R_SOURCES), $(dir) $(filter %/, $(wildcard $(dir)/*/)))
|
||||||
|
INCLUDES := $(INCLUDES) $(foreach dir,$(R_INCLUDES), $(dir) $(filter %/, $(wildcard $(dir)/*/)))
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||||
|
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.S=.o)
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(BUILD)
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@$(MAKE) -C $(ROOTDIR)/stage2ldr clean
|
||||||
|
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT)-strip.elf $(OUTPUT).bin
|
||||||
|
@rm -fr $(ROOTDIR)/superblock.img $(ROOTDIR)/superblock.img.sha
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ELFLOADER = $(ROOTDIR)/stage2ldr/stage2ldr.bin
|
||||||
|
|
||||||
|
$(ROOTDIR)/superblock.img: $(OUTPUT).bin
|
||||||
|
@echo $(notdir $@)
|
||||||
|
@python3 $(ROOTDIR)/isfshax.py $< $@
|
||||||
|
|
||||||
|
$(OUTPUT).bin: $(TARGET)-strip.elf $(ELFLOADER)
|
||||||
|
@echo $(notdir $@)
|
||||||
|
@cat $(ELFLOADER) $< > $@
|
||||||
|
|
||||||
|
$(TARGET)-strip.elf: $(TARGET).elf
|
||||||
|
@echo $(notdir $@)
|
||||||
|
@$(STRIP) $< -o $@
|
||||||
|
|
||||||
|
$(TARGET).elf: $(OFILES)
|
||||||
|
|
||||||
|
$(ELFLOADER):
|
||||||
|
@$(MAKE) -C $(ROOTDIR)/stage2ldr
|
||||||
|
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
147
isfshax.py
Normal file
147
isfshax.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
from Crypto.Hash import SHA1
|
||||||
|
|
||||||
|
stage2file = sys.argv[1]
|
||||||
|
outfile = sys.argv[2]
|
||||||
|
|
||||||
|
stage2 = bytearray(open(stage2file, "rb").read())
|
||||||
|
|
||||||
|
# create superblock header
|
||||||
|
header = struct.pack(">III",
|
||||||
|
0x53465321, # magic
|
||||||
|
0xfffffffe, # generation
|
||||||
|
0x00000000 # field_0x8
|
||||||
|
)
|
||||||
|
|
||||||
|
# mark all blocks as bad
|
||||||
|
fat = [0xFFFD] * 0x8000
|
||||||
|
|
||||||
|
# generate FST entries
|
||||||
|
def fstEntry(name, mode=2, attr=0, sub=0xFFFF, sib=0xFFFF, size=0, x1=0, uid=0, gid=0, x3=0):
|
||||||
|
return struct.pack(">12sBBHHIHHHI", name.encode('ascii'), mode, attr, sub, sib, size, x1, uid, gid, x3)
|
||||||
|
|
||||||
|
# create FST root node
|
||||||
|
fst = fstEntry(name="/", sub=1)
|
||||||
|
|
||||||
|
# create /sys/config/system.xml
|
||||||
|
fst += fstEntry(name="sys", sub=2, sib=4)
|
||||||
|
fst += fstEntry(name="config", sub=3)
|
||||||
|
fst += fstEntry(name="system.xml", mode=0xC1, sub=0x7FFF, size=0x636)
|
||||||
|
fat[0x7FFF] = 0xFFFB # prevent infinite loop in ISFS stat
|
||||||
|
|
||||||
|
# ensure IOS detects this as a *very* broken
|
||||||
|
# superblock rather than trying to repair it
|
||||||
|
# by setting one file node sub to >0xFFFB
|
||||||
|
fst += fstEntry(name="ios.stop", mode=0xC1, sib=5, sub=0xFFFC, size=1)
|
||||||
|
|
||||||
|
# create 0x69 recursive entries to overflow the stack up
|
||||||
|
# to 0x0D40E240 and overwrite FLA's FS device pointer
|
||||||
|
# with the address of the superblock
|
||||||
|
for i in range(5+0, 5+0x68):
|
||||||
|
fst += fstEntry(name="a", sub=(i+1))
|
||||||
|
fst += fstEntry(name="a")
|
||||||
|
|
||||||
|
|
||||||
|
# clear stage2 fake FST entries mode field at k*0x20 + 0xC,
|
||||||
|
# otherwise boot1 will clear the first bit of the size field
|
||||||
|
repairData = bytearray()
|
||||||
|
|
||||||
|
offs = 0x0C
|
||||||
|
while offs < len(stage2):
|
||||||
|
if (len(repairData) & 0x1F) == 0x0C:
|
||||||
|
repairData.append(0) # skip FST mode field
|
||||||
|
repairData.append(stage2[offs])
|
||||||
|
stage2[offs] = 0
|
||||||
|
offs += 0x20
|
||||||
|
|
||||||
|
|
||||||
|
# place stage1/repairData/stage2 at the end of the FST
|
||||||
|
superblockStart = 0x01f80000
|
||||||
|
superblockEnd = superblockStart + 0x40000
|
||||||
|
fstOffset = 0x1000c
|
||||||
|
fstStart = superblockStart + fstOffset
|
||||||
|
fstAreaSize = 6143 * 0x20
|
||||||
|
|
||||||
|
stage2FstOffset = (fstAreaSize - len(stage2)) & ~0x1F
|
||||||
|
stage2Addr = fstStart + stage2FstOffset
|
||||||
|
|
||||||
|
repairDataFstOffset = (stage2FstOffset - len(repairData)) & ~0x1F
|
||||||
|
repairDataAddr = fstStart + repairDataFstOffset
|
||||||
|
|
||||||
|
|
||||||
|
# stage1 adds back the removed FST mode field bytes to stage2
|
||||||
|
stage1Code = [
|
||||||
|
b"\xe2\x8f\x00\x2c", # +00 add r0, pc, #0x2C @ get address of stage1 data
|
||||||
|
b"\xe8\x90\x00\x07", # +04 ldmia r0, {r0, r1, r2} @ load repairDataAddr, repairDataEnd, stage2Addr
|
||||||
|
b"\xe2\x82\x50\x0c", # +08 add r5, r2, 0xC @ compute address of first missing byte
|
||||||
|
b"\x00\x00\x00\x00", # +0C @ skip FST mode field
|
||||||
|
b"\xe2\x00\x40\x1f", # +10 loop: and r4, r0, #0x1F @ get last 5 bits of repair data ptr
|
||||||
|
b"\xe4\xd0\x30\x01", # +14 ldrb r3, [r0], #1 @ load byte and increment repair data ptr
|
||||||
|
b"\xe3\x54\x00\x18", # +18 cmp r4, #0x18 @ ignore repair data when ptr ends with 0x18 (-> fst+0xC)
|
||||||
|
b"\x14\xc5\x30\x20", # +1C strbne r3, [r5], #0x20 @ otherwise write repair data to missing byte
|
||||||
|
b"\xe1\x50\x00\x01", # +20 cmp r0, r1 @ check if repair data finished
|
||||||
|
b"\x12\x4f\xf0\x1c", # +24 bne loop (subne pc, pc, #0x1C) @ otherwise loop
|
||||||
|
b"\xe1\x2f\xff\x12", # +28 bx r2 @ jump to stage2
|
||||||
|
b"\x00\x00\x00\x00", # +2C skip FST mode field
|
||||||
|
b"\x00\x00\x00\x00", # +30
|
||||||
|
]
|
||||||
|
stage1 = b"".join(stage1Code)
|
||||||
|
stage1 += struct.pack(">III", repairDataAddr, repairDataAddr + len(repairData), stage2Addr)
|
||||||
|
|
||||||
|
stage1FstOffset = (repairDataFstOffset - len(stage1)) & ~0x1F
|
||||||
|
stage1Addr = fstStart + stage1FstOffset
|
||||||
|
|
||||||
|
|
||||||
|
# append stage1/repairData/stage2 to the FST
|
||||||
|
if stage1FstOffset < len(fst):
|
||||||
|
print("ERROR: insufficient superblock space for stage1")
|
||||||
|
sys.exit(1)
|
||||||
|
print('(offs %#05x) %#08x -> stage1' % (fstOffset + stage1FstOffset, stage1Addr))
|
||||||
|
|
||||||
|
if repairDataFstOffset < len(fst):
|
||||||
|
print("ERROR: insufficient superblock space for repair data")
|
||||||
|
sys.exit(1)
|
||||||
|
print('(offs %#05x) %#08x -> repair data' % (fstOffset + repairDataFstOffset, repairDataAddr))
|
||||||
|
|
||||||
|
if stage2FstOffset < len(fst):
|
||||||
|
print("ERROR: insufficient superblock space for stage2")
|
||||||
|
sys.exit(1)
|
||||||
|
print('(offs %#05x) %#08x -> stage2' % (fstOffset + stage2FstOffset, stage2Addr))
|
||||||
|
|
||||||
|
fst += b"\x00" * (stage1FstOffset - len(fst))
|
||||||
|
fst += stage1
|
||||||
|
fst += b"\x00" * (repairDataFstOffset - len(fst))
|
||||||
|
fst += repairData
|
||||||
|
fst += b"\x00" * (stage2FstOffset - len(fst))
|
||||||
|
fst += stage2
|
||||||
|
fst += b"\x00" * (fstAreaSize - len(fst))
|
||||||
|
|
||||||
|
|
||||||
|
# write stage1 entrypoint to the first two FAT entries; after
|
||||||
|
# the stack overflow overwrites FLA's FS device pointer with
|
||||||
|
# the address of the superblock, they will be interpreted as
|
||||||
|
# as the structure's read() function pointer
|
||||||
|
fat[0] = stage1Addr >> 16
|
||||||
|
fat[1] = stage1Addr & 0xffff
|
||||||
|
|
||||||
|
|
||||||
|
# create superblock
|
||||||
|
superblock = header
|
||||||
|
superblock += struct.pack(">32768H", *fat)
|
||||||
|
superblock += fst
|
||||||
|
superblock += b"\x00" * 0x14
|
||||||
|
|
||||||
|
# write crafted superblock
|
||||||
|
f = open(outfile, "wb")
|
||||||
|
f.write(superblock)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# write crafted superblock hash
|
||||||
|
h = SHA1.new(superblock)
|
||||||
|
f = open(outfile + ".sha", "wb")
|
||||||
|
f.write(h.digest())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
print("isfshax: written superblock to " + outfile)
|
102
stage2/aes.c
Normal file
102
stage2/aes.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "aes.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#define AES_CMD_RESET 0
|
||||||
|
#define AES_CMD_ENCRYPT 0x9000
|
||||||
|
#define AES_CMD_DECRYPT 0x9800
|
||||||
|
|
||||||
|
static inline void aes_command(u16 cmd, u8 iv_keep, u32 blocks)
|
||||||
|
{
|
||||||
|
if (blocks != 0)
|
||||||
|
blocks--;
|
||||||
|
write32(AES_CTRL, (cmd << 16) | (iv_keep ? 0x1000 : 0) | (blocks&0x7f));
|
||||||
|
while (read32(AES_CTRL) & 0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void aes_reset(void)
|
||||||
|
{
|
||||||
|
write32(AES_CTRL, 0);
|
||||||
|
while (read32(AES_CTRL) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void aes_set_iv(u8 *iv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 4; i++) {
|
||||||
|
write32(AES_IV, *(u32 *)iv);
|
||||||
|
iv += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void aes_empty_iv(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 4; i++)
|
||||||
|
write32(AES_IV, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void aes_set_key(u8 *key)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 4; i++) {
|
||||||
|
write32(AES_KEY, *(u32 *)key);
|
||||||
|
key += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void aes_decrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv)
|
||||||
|
{
|
||||||
|
dc_flushrange(src, blocks * 16);
|
||||||
|
dc_invalidaterange(dst, blocks * 16);
|
||||||
|
ahb_flush_to(RB_AES);
|
||||||
|
|
||||||
|
int this_blocks = 0;
|
||||||
|
while(blocks > 0) {
|
||||||
|
this_blocks = blocks;
|
||||||
|
if (this_blocks > 0x80)
|
||||||
|
this_blocks = 0x80;
|
||||||
|
|
||||||
|
write32(AES_SRC, dma_addr(src));
|
||||||
|
write32(AES_DEST, dma_addr(dst));
|
||||||
|
|
||||||
|
aes_command(AES_CMD_DECRYPT, keep_iv, this_blocks);
|
||||||
|
|
||||||
|
blocks -= this_blocks;
|
||||||
|
src += this_blocks<<4;
|
||||||
|
dst += this_blocks<<4;
|
||||||
|
keep_iv = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ahb_flush_from(WB_AES);
|
||||||
|
ahb_flush_to(RB_IOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void aes_encrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv)
|
||||||
|
{
|
||||||
|
dc_flushrange(src, blocks * 16);
|
||||||
|
dc_invalidaterange(dst, blocks * 16);
|
||||||
|
ahb_flush_to(RB_AES);
|
||||||
|
|
||||||
|
int this_blocks = 0;
|
||||||
|
while(blocks > 0) {
|
||||||
|
this_blocks = blocks;
|
||||||
|
if (this_blocks > 0x80)
|
||||||
|
this_blocks = 0x80;
|
||||||
|
|
||||||
|
write32(AES_SRC, dma_addr(src));
|
||||||
|
write32(AES_DEST, dma_addr(dst));
|
||||||
|
|
||||||
|
aes_command(AES_CMD_ENCRYPT, keep_iv, this_blocks);
|
||||||
|
|
||||||
|
blocks -= this_blocks;
|
||||||
|
src += this_blocks<<4;
|
||||||
|
dst += this_blocks<<4;
|
||||||
|
keep_iv = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ahb_flush_from(WB_AES);
|
||||||
|
ahb_flush_to(RB_IOD);
|
||||||
|
}
|
29
stage2/aes.h
Normal file
29
stage2/aes.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AES_H__
|
||||||
|
#define __AES_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define AES_BLOCK_SIZE 16
|
||||||
|
|
||||||
|
void aes_reset(void);
|
||||||
|
void aes_set_iv(u8 *iv);
|
||||||
|
void aes_empty_iv();
|
||||||
|
void aes_set_key(u8 *key);
|
||||||
|
void aes_decrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv);
|
||||||
|
void aes_encrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv);
|
||||||
|
|
||||||
|
#endif /* __AES_H__ */
|
69
stage2/ancast.c
Normal file
69
stage2/ancast.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "sha.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "smc.h"
|
||||||
|
|
||||||
|
#include "ancast.h"
|
||||||
|
|
||||||
|
|
||||||
|
u32 ancast_iop_load(u8* buffer, size_t size)
|
||||||
|
{
|
||||||
|
u32 magic = read32((u32) buffer);
|
||||||
|
if(magic != ANCAST_MAGIC) {
|
||||||
|
DEBUG("ancast_iop_load: not an ancast image (magic is 0x%08lX, expected 0x%08lX).\n", magic, ANCAST_MAGIC);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 sig_offset = read32((u32) &buffer[0x08]);
|
||||||
|
u32 sig_type = read32((u32) &buffer[sig_offset]);
|
||||||
|
|
||||||
|
if (sig_type != 0x02) {
|
||||||
|
DEBUG("ancast_iop_load: unexpected signature type 0x%02lX.\n", sig_type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ancast_header* ancast = (ancast_header*)&buffer[0x1A0];
|
||||||
|
u8 target = ancast->device >> 4;
|
||||||
|
|
||||||
|
if(target != ANCAST_TARGET_IOP) {
|
||||||
|
DEBUG("ancast: not an IOP image (target is 0x%02X, expected 0x%02X).\n", target, ANCAST_TARGET_IOP);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* body = (u8*)(ancast + 1);
|
||||||
|
|
||||||
|
u32 hash[SHA_HASH_WORDS] = {0};
|
||||||
|
sha_hash(body, hash, ancast->body_size);
|
||||||
|
|
||||||
|
u32* h1 = ancast->body_hash;
|
||||||
|
u32* h2 = hash;
|
||||||
|
if(memcmp(h1, h2, SHA_HASH_SIZE) != 0) {
|
||||||
|
DEBUG("ancast: body hash check failed.\n");
|
||||||
|
DEBUG(" expected: %08lX%08lX%08lX%08lX%08lX\n", h1[0], h1[1], h1[2], h1[3], h1[4]);
|
||||||
|
DEBUG(" calculated: %08lX%08lX%08lX%08lX%08lX\n", h2[0], h2[1], h2[2], h2[3], h2[4]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ios_header* header = (ios_header*)body;
|
||||||
|
u32 vector = (u32) &body[header->header_size];
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
42
stage2/ancast.h
Normal file
42
stage2/ancast.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ANCAST_H
|
||||||
|
#define _ANCAST_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "sha.h"
|
||||||
|
|
||||||
|
#define ANCAST_MAGIC (0xEFA282D9l)
|
||||||
|
#define ANCAST_TARGET_IOP (0x02)
|
||||||
|
#define ANCAST_ADDRESS_IOP (0x01000000)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u16 unk1;
|
||||||
|
u8 unk2;
|
||||||
|
u8 unk3;
|
||||||
|
u32 device;
|
||||||
|
u32 type;
|
||||||
|
u32 body_size;
|
||||||
|
u32 body_hash[SHA_HASH_WORDS];
|
||||||
|
u32 version;
|
||||||
|
u8 padding[0x38];
|
||||||
|
} ancast_header;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 header_size;
|
||||||
|
u32 loader_size;
|
||||||
|
u32 elf_size;
|
||||||
|
u32 ddr_init;
|
||||||
|
} ios_header;
|
||||||
|
|
||||||
|
u32 ancast_iop_load(u8* buffer, size_t size);
|
||||||
|
|
||||||
|
#endif
|
37
stage2/bsdtypes.h
Normal file
37
stage2/bsdtypes.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BSDTYPES_H__
|
||||||
|
#define __BSDTYPES_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "errno.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
//typedef u32 u_int;
|
||||||
|
typedef u32 u_int32_t;
|
||||||
|
typedef u16 u_int16_t;
|
||||||
|
typedef u8 u_int8_t;
|
||||||
|
typedef u8 u_char;
|
||||||
|
|
||||||
|
typedef u32 bus_space_tag_t;
|
||||||
|
typedef u32 bus_space_handle_t;
|
||||||
|
|
||||||
|
#define MIN(a, b) (((a)>(b))?(b):(a))
|
||||||
|
|
||||||
|
#define wakeup(...)
|
||||||
|
|
||||||
|
#define ISSET(var, mask) (((var) & (mask)) ? 1 : 0)
|
||||||
|
#define SET(var, mask) ((var) |= (mask))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
36
stage2/crypto.c
Normal file
36
stage2/crypto.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "aes.h"
|
||||||
|
|
||||||
|
otp_t otp;
|
||||||
|
|
||||||
|
void crypto_read_otp(void)
|
||||||
|
{
|
||||||
|
u32 *otpd = (u32*)&otp;
|
||||||
|
int word, bank;
|
||||||
|
for (bank = 0; bank < 8; bank++)
|
||||||
|
{
|
||||||
|
for (word = 0; word < 0x20; word++)
|
||||||
|
{
|
||||||
|
write32(LT_OTPCMD, 0x80000000 | bank << 8 | word);
|
||||||
|
*otpd++ = read32(LT_OTPDATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
stage2/crypto.h
Normal file
136
stage2/crypto.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CRYPTO_H__
|
||||||
|
#define __CRYPTO_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Bank 0 (Wii)
|
||||||
|
struct {
|
||||||
|
u8 wii_boot1_hash[20];
|
||||||
|
u8 wii_common_key[16];
|
||||||
|
u32 wii_ng_id;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u8 wii_ng_priv[30];
|
||||||
|
u8 _wii_wtf1[18];
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
u8 _wii_wtf2[28];
|
||||||
|
u8 wii_nand_hmac[20];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
u8 wii_nand_key[16];
|
||||||
|
u8 wii_rng_key[16];
|
||||||
|
u32 wii_unk1;
|
||||||
|
u32 wii_unk2; // 0x00000007
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 1 (Wii U)
|
||||||
|
struct {
|
||||||
|
u32 security_level;
|
||||||
|
u32 iostrength_flag;
|
||||||
|
u32 seeprom_pulse;
|
||||||
|
u32 unk1;
|
||||||
|
u8 fw_ancast_key[16];
|
||||||
|
u8 seeprom_key[16];
|
||||||
|
u8 unk2[16];
|
||||||
|
u8 unk3[16];
|
||||||
|
u8 vwii_common_key[16];
|
||||||
|
u8 common_key[16];
|
||||||
|
u8 unk4[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 2 (Wii U)
|
||||||
|
struct {
|
||||||
|
u8 unk5[16];
|
||||||
|
u8 unk6[16];
|
||||||
|
u8 ssl_master_key[16];
|
||||||
|
u8 external_master_key[16];
|
||||||
|
u8 unk7[16];
|
||||||
|
u8 xor_key[16];
|
||||||
|
u8 rng_key[16];
|
||||||
|
u8 nand_key[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 3 (Wii U)
|
||||||
|
struct {
|
||||||
|
u8 emmc_key[16];
|
||||||
|
u8 dev_master_key[16];
|
||||||
|
u8 drh_key[16];
|
||||||
|
u8 unk8[48];
|
||||||
|
u8 nand_hmac[20];
|
||||||
|
u8 unk9[12];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 4 (Wii U)
|
||||||
|
struct {
|
||||||
|
u8 unk10[16];
|
||||||
|
union {
|
||||||
|
u8 external_seed_full[16];
|
||||||
|
struct {
|
||||||
|
u8 _wtf1[12];
|
||||||
|
u8 external_seed[4];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
u8 vwii_ng_priv[32];
|
||||||
|
u8 unk11[32];
|
||||||
|
union {
|
||||||
|
u8 rng_seed_full[16];
|
||||||
|
struct {
|
||||||
|
u32 rng_seed;
|
||||||
|
u8 _wtf2[12];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
u8 unk12[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 5 (Wii U)
|
||||||
|
struct {
|
||||||
|
u32 rootca_version;
|
||||||
|
u32 rootca_ms;
|
||||||
|
u32 unk13;
|
||||||
|
u8 rootca_signature[64];
|
||||||
|
u8 unk14[20];
|
||||||
|
u8 unk15[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 6 (Wii SEEPROM)
|
||||||
|
struct {
|
||||||
|
u8 wii_seeprom_certificate[96];
|
||||||
|
u8 wii_seeprom_signature[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bank 7 (Misc.)
|
||||||
|
struct {
|
||||||
|
u8 unk16[32];
|
||||||
|
u8 boot1_key[16];
|
||||||
|
u8 unk17[16];
|
||||||
|
u8 _empty1[32];
|
||||||
|
u8 unk18[16];
|
||||||
|
char ascii_tag[12];
|
||||||
|
u32 jtag_status;
|
||||||
|
};
|
||||||
|
} __attribute__((packed, aligned(4))) otp_t;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(otp_t) == 0x400, "OTP size must be 0x400!");
|
||||||
|
|
||||||
|
extern otp_t otp;
|
||||||
|
|
||||||
|
void crypto_read_otp();
|
||||||
|
|
||||||
|
#endif
|
15
stage2/debug.c
Normal file
15
stage2/debug.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifdef LOLSERIAL_DEBUG
|
||||||
|
|
||||||
|
extern void lolserial_lprint(char *str, int len);
|
||||||
|
|
||||||
|
void DEBUG(char *fmt, ...)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
vsnprintf(str, sizeof(str), fmt, va);
|
||||||
|
lolserial_lprint(str, sizeof(str));
|
||||||
|
va_end(va);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LOLSERIAL_DEBUG */
|
27
stage2/debug.h
Normal file
27
stage2/debug.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DEBUG_H__
|
||||||
|
#define __DEBUG_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifndef LOLSERIAL_DEBUG
|
||||||
|
static inline void DEBUG(char *fmt, ...) {}
|
||||||
|
#else
|
||||||
|
void DEBUG(char *fmt, ...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __DEBUG_H__ */
|
167
stage2/fatfs/diskio.c
Normal file
167
stage2/fatfs/diskio.c
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* If a working storage control module is available, it should be */
|
||||||
|
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||||
|
/* This is an example of glue functions to attach various exsisting */
|
||||||
|
/* storage control modules to the FatFs module with a defined API. */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "diskio.h" /* FatFs lower layer API */
|
||||||
|
#include "ff.h"
|
||||||
|
|
||||||
|
#include <wctype.h>
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
#include "sdcard.h"
|
||||||
|
#include "sdhc.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
static u8 buffer[SDMMC_DEFAULT_BLOCKLEN * SDHC_BLOCK_COUNT_MAX] ALIGNED(32);
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Get Disk Status */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
DSTATUS disk_status (
|
||||||
|
BYTE pdrv /* Physical drive number to identify the drive */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
(void)pdrv;
|
||||||
|
|
||||||
|
switch(sdcard_check_card()) {
|
||||||
|
case SDMMC_INSERTED:
|
||||||
|
return 0;
|
||||||
|
case SDMMC_NEW_CARD:
|
||||||
|
return STA_NOINIT;
|
||||||
|
default:
|
||||||
|
return STA_NODISK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Initialize a Drive */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
DSTATUS disk_initialize (
|
||||||
|
BYTE pdrv /* Physical drive number to identify the drive */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (sdcard_check_card() == SDMMC_NO_CARD)
|
||||||
|
return STA_NODISK;
|
||||||
|
|
||||||
|
sdcard_ack_card();
|
||||||
|
return disk_status(pdrv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Read Sector(s) */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
DRESULT disk_read (
|
||||||
|
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||||
|
BYTE *buff, /* Data buffer to store read data */
|
||||||
|
DWORD sector, /* Sector address in LBA */
|
||||||
|
UINT count /* Number of sectors to read */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
(void)pdrv;
|
||||||
|
|
||||||
|
while(count) {
|
||||||
|
u32 work = min(count, SDHC_BLOCK_COUNT_MAX);
|
||||||
|
|
||||||
|
if(sdcard_read(sector, work, buffer) != 0)
|
||||||
|
return RES_ERROR;
|
||||||
|
|
||||||
|
memcpy(buff, buffer, work * SDMMC_DEFAULT_BLOCKLEN);
|
||||||
|
|
||||||
|
sector += work;
|
||||||
|
count -= work;
|
||||||
|
buff += work * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Write Sector(s) */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if _USE_WRITE
|
||||||
|
DRESULT disk_write (
|
||||||
|
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||||
|
const BYTE *buff, /* Data to be written */
|
||||||
|
DWORD sector, /* Sector address in LBA */
|
||||||
|
UINT count /* Number of sectors to write */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
(void)pdrv;
|
||||||
|
|
||||||
|
while(count) {
|
||||||
|
u32 work = min(count, SDHC_BLOCK_COUNT_MAX);
|
||||||
|
|
||||||
|
memcpy(buffer, buff, work * SDMMC_DEFAULT_BLOCKLEN);
|
||||||
|
|
||||||
|
if(sdcard_write(sector, work, buffer) != 0)
|
||||||
|
return RES_ERROR;
|
||||||
|
|
||||||
|
sector += work;
|
||||||
|
count -= work;
|
||||||
|
buff += work * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
/* Miscellaneous Functions */
|
||||||
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if _USE_IOCTL
|
||||||
|
DRESULT disk_ioctl (
|
||||||
|
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||||
|
BYTE cmd, /* Control code */
|
||||||
|
void *buff /* Buffer to send/receive control data */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
(void)pdrv;
|
||||||
|
|
||||||
|
if (cmd == CTRL_SYNC)
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
if (cmd == GET_SECTOR_SIZE) {
|
||||||
|
*(u32*)buff = SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd == GET_BLOCK_SIZE) {
|
||||||
|
*(u32*)buff = 1;
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd == GET_SECTOR_COUNT) {
|
||||||
|
int sectors = sdcard_get_sectors();
|
||||||
|
if(sectors < 0) return RES_ERROR;
|
||||||
|
*(u32*)buff = sectors;
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DWORD get_fattime()
|
||||||
|
{
|
||||||
|
// NO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "option/unicode.c"
|
79
stage2/fatfs/diskio.h
Normal file
79
stage2/fatfs/diskio.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*-----------------------------------------------------------------------/
|
||||||
|
/ Low level disk interface modlue include file (C)ChaN, 2014 /
|
||||||
|
/-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef _DISKIO_DEFINED
|
||||||
|
#define _DISKIO_DEFINED
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _USE_WRITE 1 /* 1: Enable disk_write function */
|
||||||
|
#define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */
|
||||||
|
|
||||||
|
#include "integer.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Status of Disk Functions */
|
||||||
|
typedef BYTE DSTATUS;
|
||||||
|
|
||||||
|
/* Results of Disk Functions */
|
||||||
|
typedef enum {
|
||||||
|
RES_OK = 0, /* 0: Successful */
|
||||||
|
RES_ERROR, /* 1: R/W Error */
|
||||||
|
RES_WRPRT, /* 2: Write Protected */
|
||||||
|
RES_NOTRDY, /* 3: Not Ready */
|
||||||
|
RES_PARERR /* 4: Invalid Parameter */
|
||||||
|
} DRESULT;
|
||||||
|
|
||||||
|
/*---------------------------------------*/
|
||||||
|
/* Prototypes for disk control functions */
|
||||||
|
|
||||||
|
|
||||||
|
DSTATUS disk_initialize (BYTE pdrv);
|
||||||
|
DSTATUS disk_status (BYTE pdrv);
|
||||||
|
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||||
|
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||||
|
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||||
|
|
||||||
|
|
||||||
|
/* Disk Status Bits (DSTATUS) */
|
||||||
|
|
||||||
|
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||||
|
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||||
|
#define STA_PROTECT 0x04 /* Write protected */
|
||||||
|
|
||||||
|
|
||||||
|
/* Command code for disk_ioctrl fucntion */
|
||||||
|
|
||||||
|
/* Generic command (Used by FatFs) */
|
||||||
|
#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
|
||||||
|
#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
|
||||||
|
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
|
||||||
|
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
|
||||||
|
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
|
||||||
|
|
||||||
|
/* Generic command (Not used by FatFs) */
|
||||||
|
#define CTRL_POWER 5 /* Get/Set power status */
|
||||||
|
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||||
|
#define CTRL_EJECT 7 /* Eject media */
|
||||||
|
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||||
|
|
||||||
|
/* MMC/SDC specific ioctl command */
|
||||||
|
#define MMC_GET_TYPE 10 /* Get card type */
|
||||||
|
#define MMC_GET_CSD 11 /* Get CSD */
|
||||||
|
#define MMC_GET_CID 12 /* Get CID */
|
||||||
|
#define MMC_GET_OCR 13 /* Get OCR */
|
||||||
|
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||||
|
|
||||||
|
/* ATA/CF specific ioctl command */
|
||||||
|
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||||
|
#define ATA_GET_MODEL 21 /* Get model name */
|
||||||
|
#define ATA_GET_SN 22 /* Get serial number */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
4683
stage2/fatfs/ff.c
Normal file
4683
stage2/fatfs/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
350
stage2/fatfs/ff.h
Normal file
350
stage2/fatfs/ff.h
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ FatFs - FAT file system module include R0.11a (C)ChaN, 2015
|
||||||
|
/----------------------------------------------------------------------------/
|
||||||
|
/ FatFs module is a free software that opened under license policy of
|
||||||
|
/ following conditions.
|
||||||
|
/
|
||||||
|
/ Copyright (C) 2015, ChaN, all right reserved.
|
||||||
|
/
|
||||||
|
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
/ this condition and the following disclaimer.
|
||||||
|
/
|
||||||
|
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||||
|
/ and any warranties related to this software are DISCLAIMED.
|
||||||
|
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||||
|
/ by use of this software.
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _FATFS
|
||||||
|
#define _FATFS 64180 /* Revision ID */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "integer.h" /* Basic integer types */
|
||||||
|
#include "ffconf.h" /* FatFs configuration options */
|
||||||
|
#if _FATFS != _FFCONF
|
||||||
|
#error Wrong configuration file (ffconf.h).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions of volume management */
|
||||||
|
|
||||||
|
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||||
|
typedef struct {
|
||||||
|
BYTE pd; /* Physical drive number */
|
||||||
|
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||||
|
} PARTITION;
|
||||||
|
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||||
|
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||||
|
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||||
|
|
||||||
|
#else /* Single partition configuration */
|
||||||
|
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||||
|
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Type of path name strings on FatFs API */
|
||||||
|
|
||||||
|
#if _LFN_UNICODE /* Unicode string */
|
||||||
|
#if !_USE_LFN
|
||||||
|
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||||
|
#endif
|
||||||
|
#ifndef _INC_TCHAR
|
||||||
|
typedef WCHAR TCHAR;
|
||||||
|
#define _T(x) L ## x
|
||||||
|
#define _TEXT(x) L ## x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else /* ANSI/OEM string */
|
||||||
|
#ifndef _INC_TCHAR
|
||||||
|
typedef char TCHAR;
|
||||||
|
#define _T(x) x
|
||||||
|
#define _TEXT(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File system object structure (FATFS) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
|
||||||
|
BYTE drv; /* Physical drive number */
|
||||||
|
BYTE csize; /* Sectors per cluster (1,2,4...128) */
|
||||||
|
BYTE n_fats; /* Number of FAT copies (1 or 2) */
|
||||||
|
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||||
|
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||||
|
WORD id; /* File system mount ID */
|
||||||
|
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||||
|
#if _MAX_SS != _MIN_SS
|
||||||
|
WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */
|
||||||
|
#endif
|
||||||
|
#if _FS_REENTRANT
|
||||||
|
_SYNC_t sobj; /* Identifier of sync object */
|
||||||
|
#endif
|
||||||
|
#if !_FS_READONLY
|
||||||
|
DWORD last_clust; /* Last allocated cluster */
|
||||||
|
DWORD free_clust; /* Number of free clusters */
|
||||||
|
#endif
|
||||||
|
#if _FS_RPATH
|
||||||
|
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||||
|
#endif
|
||||||
|
DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */
|
||||||
|
DWORD fsize; /* Sectors per FAT */
|
||||||
|
DWORD volbase; /* Volume start sector */
|
||||||
|
DWORD fatbase; /* FAT start sector */
|
||||||
|
DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
|
||||||
|
DWORD database; /* Data start sector */
|
||||||
|
DWORD winsect; /* Current sector appearing in the win[] */
|
||||||
|
BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||||
|
} FATFS;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File object structure (FIL) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FATFS* fs; /* Pointer to the related file system object (**do not change order**) */
|
||||||
|
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||||
|
BYTE flag; /* Status flags */
|
||||||
|
BYTE err; /* Abort flag (error code) */
|
||||||
|
DWORD fptr; /* File read/write pointer (Zeroed on file open) */
|
||||||
|
DWORD fsize; /* File size */
|
||||||
|
DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
|
||||||
|
DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */
|
||||||
|
DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */
|
||||||
|
#if !_FS_READONLY
|
||||||
|
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||||
|
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
|
||||||
|
#endif
|
||||||
|
#if _USE_FASTSEEK
|
||||||
|
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||||
|
#endif
|
||||||
|
#if _FS_LOCK
|
||||||
|
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||||
|
#endif
|
||||||
|
#if !_FS_TINY
|
||||||
|
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||||
|
#endif
|
||||||
|
} FIL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Directory object structure (FDIR) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */
|
||||||
|
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||||
|
WORD index; /* Current read/write index number */
|
||||||
|
DWORD sclust; /* Table start cluster (0:Root dir) */
|
||||||
|
DWORD clust; /* Current cluster */
|
||||||
|
DWORD sect; /* Current sector */
|
||||||
|
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
|
||||||
|
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
|
||||||
|
#if _FS_LOCK
|
||||||
|
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||||
|
#endif
|
||||||
|
#if _USE_LFN
|
||||||
|
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||||
|
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
|
||||||
|
#endif
|
||||||
|
#if _USE_FIND
|
||||||
|
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||||
|
#endif
|
||||||
|
} FDIR;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File information structure (FILINFO) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
DWORD fsize; /* File size */
|
||||||
|
WORD fdate; /* Last modified date */
|
||||||
|
WORD ftime; /* Last modified time */
|
||||||
|
BYTE fattrib; /* Attribute */
|
||||||
|
TCHAR fname[13]; /* Short file name (8.3 format) */
|
||||||
|
#if _USE_LFN
|
||||||
|
TCHAR* lfname; /* Pointer to the LFN buffer */
|
||||||
|
UINT lfsize; /* Size of LFN buffer in TCHAR */
|
||||||
|
#endif
|
||||||
|
} FILINFO;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File function return code (FRESULT) */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FR_OK = 0, /* (0) Succeeded */
|
||||||
|
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||||
|
FR_INT_ERR, /* (2) Assertion failed */
|
||||||
|
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||||
|
FR_NO_FILE, /* (4) Could not find the file */
|
||||||
|
FR_NO_PATH, /* (5) Could not find the path */
|
||||||
|
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||||
|
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||||
|
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||||
|
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||||
|
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||||
|
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||||
|
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||||
|
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||||
|
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||||
|
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||||
|
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||||
|
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||||
|
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */
|
||||||
|
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||||
|
} FRESULT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* FatFs module application interface */
|
||||||
|
|
||||||
|
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||||
|
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||||
|
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||||
|
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||||
|
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||||
|
FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */
|
||||||
|
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||||
|
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||||
|
FRESULT f_opendir (FDIR* dp, const TCHAR* path); /* Open a directory */
|
||||||
|
FRESULT f_closedir (FDIR* dp); /* Close an open directory */
|
||||||
|
FRESULT f_readdir (FDIR* dp, FILINFO* fno); /* Read a directory item */
|
||||||
|
FRESULT f_findfirst (FDIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||||
|
FRESULT f_findnext (FDIR* dp, FILINFO* fno); /* Find next file */
|
||||||
|
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||||
|
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||||
|
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||||
|
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||||
|
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */
|
||||||
|
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */
|
||||||
|
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||||
|
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||||
|
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||||
|
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||||
|
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||||
|
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||||
|
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||||
|
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au, UINT start, UINT end); /* Create a file system on the volume */
|
||||||
|
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||||
|
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||||
|
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||||
|
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||||
|
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||||
|
|
||||||
|
#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize))
|
||||||
|
#define f_error(fp) ((fp)->err)
|
||||||
|
#define f_tell(fp) ((fp)->fptr)
|
||||||
|
#define f_size(fp) ((fp)->fsize)
|
||||||
|
#define f_rewind(fp) f_lseek((fp), 0)
|
||||||
|
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||||
|
|
||||||
|
#ifndef EOF
|
||||||
|
#define EOF (-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* Additional user defined functions */
|
||||||
|
|
||||||
|
/* RTC function */
|
||||||
|
#if !_FS_READONLY && !_FS_NORTC
|
||||||
|
DWORD get_fattime (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Unicode support functions */
|
||||||
|
#if _USE_LFN /* Unicode - OEM code conversion */
|
||||||
|
WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */
|
||||||
|
WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */
|
||||||
|
#if _USE_LFN == 3 /* Memory functions */
|
||||||
|
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||||
|
void ff_memfree (void* mblock); /* Free memory block */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Sync functions */
|
||||||
|
#if _FS_REENTRANT
|
||||||
|
int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */
|
||||||
|
int ff_req_grant (_SYNC_t sobj); /* Lock sync object */
|
||||||
|
void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */
|
||||||
|
int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* Flags and offset address */
|
||||||
|
|
||||||
|
|
||||||
|
/* File access control and file status flags (FIL.flag) */
|
||||||
|
|
||||||
|
#define FA_READ 0x01
|
||||||
|
#define FA_OPEN_EXISTING 0x00
|
||||||
|
|
||||||
|
#if !_FS_READONLY
|
||||||
|
#define FA_WRITE 0x02
|
||||||
|
#define FA_CREATE_NEW 0x04
|
||||||
|
#define FA_CREATE_ALWAYS 0x08
|
||||||
|
#define FA_OPEN_ALWAYS 0x10
|
||||||
|
#define FA__WRITTEN 0x20
|
||||||
|
#define FA__DIRTY 0x40
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* FAT sub type (FATFS.fs_type) */
|
||||||
|
|
||||||
|
#define FS_FAT12 1
|
||||||
|
#define FS_FAT16 2
|
||||||
|
#define FS_FAT32 3
|
||||||
|
|
||||||
|
|
||||||
|
/* File attribute bits for directory entry */
|
||||||
|
|
||||||
|
#define AM_RDO 0x01 /* Read only */
|
||||||
|
#define AM_HID 0x02 /* Hidden */
|
||||||
|
#define AM_SYS 0x04 /* System */
|
||||||
|
#define AM_VOL 0x08 /* Volume label */
|
||||||
|
#define AM_LFN 0x0F /* LFN entry */
|
||||||
|
#define AM_DIR 0x10 /* Directory */
|
||||||
|
#define AM_ARC 0x20 /* Archive */
|
||||||
|
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||||
|
|
||||||
|
|
||||||
|
/* Fast seek feature */
|
||||||
|
#define CREATE_LINKMAP 0xFFFFFFFF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------*/
|
||||||
|
/* Multi-byte word access macros */
|
||||||
|
|
||||||
|
#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
|
||||||
|
#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
|
||||||
|
#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
|
||||||
|
#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
|
||||||
|
#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
|
||||||
|
#else /* Use byte-by-byte access to the FAT structure */
|
||||||
|
#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
|
||||||
|
#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
|
||||||
|
#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
|
||||||
|
#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _FATFS */
|
276
stage2/fatfs/ffconf.h
Normal file
276
stage2/fatfs/ffconf.h
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _FFCONF 64180 /* Revision ID */
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Function Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _FS_READONLY 1
|
||||||
|
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||||
|
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||||
|
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||||
|
/ and optional writing functions as well. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_MINIMIZE 2
|
||||||
|
/* This option defines minimization level to remove some basic API functions.
|
||||||
|
/
|
||||||
|
/ 0: All basic functions are enabled.
|
||||||
|
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||||
|
/ f_truncate() and f_rename() function are removed.
|
||||||
|
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||||
|
/ 3: f_lseek() function is removed in addition to 2. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_STRFUNC 1
|
||||||
|
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
|
||||||
|
/ f_printf().
|
||||||
|
/
|
||||||
|
/ 0: Disable string functions.
|
||||||
|
/ 1: Enable without LF-CRLF conversion.
|
||||||
|
/ 2: Enable with LF-CRLF conversion. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_FIND 1
|
||||||
|
/* This option switches filtered directory read feature and related functions,
|
||||||
|
/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_MKFS 0
|
||||||
|
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_FASTSEEK 1
|
||||||
|
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_LABEL 0
|
||||||
|
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||||
|
/ (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_FORWARD 0
|
||||||
|
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
||||||
|
/ To enable it, also _FS_TINY need to be set to 1. */
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Locale and Namespace Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _CODE_PAGE 932
|
||||||
|
/* This option specifies the OEM code page to be used on the target system.
|
||||||
|
/ Incorrect setting of the code page can cause a file open failure.
|
||||||
|
/
|
||||||
|
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||||
|
/ 437 - U.S.
|
||||||
|
/ 720 - Arabic
|
||||||
|
/ 737 - Greek
|
||||||
|
/ 771 - KBL
|
||||||
|
/ 775 - Baltic
|
||||||
|
/ 850 - Latin 1
|
||||||
|
/ 852 - Latin 2
|
||||||
|
/ 855 - Cyrillic
|
||||||
|
/ 857 - Turkish
|
||||||
|
/ 860 - Portuguese
|
||||||
|
/ 861 - Icelandic
|
||||||
|
/ 862 - Hebrew
|
||||||
|
/ 863 - Canadian French
|
||||||
|
/ 864 - Arabic
|
||||||
|
/ 865 - Nordic
|
||||||
|
/ 866 - Russian
|
||||||
|
/ 869 - Greek 2
|
||||||
|
/ 932 - Japanese (DBCS)
|
||||||
|
/ 936 - Simplified Chinese (DBCS)
|
||||||
|
/ 949 - Korean (DBCS)
|
||||||
|
/ 950 - Traditional Chinese (DBCS)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_LFN 1
|
||||||
|
#define _MAX_LFN 255
|
||||||
|
/* The _USE_LFN option switches the LFN feature.
|
||||||
|
/
|
||||||
|
/ 0: Disable LFN feature. _MAX_LFN has no effect.
|
||||||
|
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||||
|
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||||
|
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||||
|
/
|
||||||
|
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
|
||||||
|
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
|
||||||
|
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||||
|
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||||
|
/ ff_memfree(), must be added to the project. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _LFN_UNICODE 0
|
||||||
|
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
|
||||||
|
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
|
||||||
|
/ to 1. This option also affects behavior of string I/O functions. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _STRF_ENCODE 0
|
||||||
|
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
|
||||||
|
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||||
|
/
|
||||||
|
/ 0: ANSI/OEM
|
||||||
|
/ 1: UTF-16LE
|
||||||
|
/ 2: UTF-16BE
|
||||||
|
/ 3: UTF-8
|
||||||
|
/
|
||||||
|
/ When _LFN_UNICODE is 0, this option has no effect. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_RPATH 0
|
||||||
|
/* This option configures relative path feature.
|
||||||
|
/
|
||||||
|
/ 0: Disable relative path feature and remove related functions.
|
||||||
|
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
|
||||||
|
/ 2: f_getcwd() function is available in addition to 1.
|
||||||
|
/
|
||||||
|
/ Note that directory items read via f_readdir() are affected by this option. */
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ Drive/Volume Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _VOLUMES 1
|
||||||
|
/* Number of volumes (logical drives) to be used. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _STR_VOLUME_ID 1
|
||||||
|
#define _VOLUME_STRS "SDMC"
|
||||||
|
/* _STR_VOLUME_ID option switches string volume ID feature.
|
||||||
|
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
|
||||||
|
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
|
||||||
|
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
|
||||||
|
/ the drive ID strings are: A-Z and 0-9. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _MULTI_PARTITION 0
|
||||||
|
/* This option switches multi-partition feature. By default (0), each logical drive
|
||||||
|
/ number is bound to the same physical drive number and only an FAT volume found on
|
||||||
|
/ the physical drive will be mounted. When multi-partition feature is enabled (1),
|
||||||
|
/ each logical drive number is bound to arbitrary physical drive and partition
|
||||||
|
/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _MIN_SS 512
|
||||||
|
#define _MAX_SS 512
|
||||||
|
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||||
|
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||||
|
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||||
|
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||||
|
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||||
|
/ disk_ioctl() function. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _USE_TRIM 0
|
||||||
|
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
|
||||||
|
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
|
||||||
|
/ disk_ioctl() function. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_NOFSINFO 0
|
||||||
|
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||||
|
/ option, and f_getfree() function at first time after volume mount will force
|
||||||
|
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||||
|
/
|
||||||
|
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||||
|
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||||
|
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||||
|
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------/
|
||||||
|
/ System Configurations
|
||||||
|
/---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define _FS_TINY 0
|
||||||
|
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||||
|
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
|
||||||
|
/ bytes. Instead of private sector buffer eliminated from the file object,
|
||||||
|
/ common sector buffer in the file system object (FATFS) is used for the file
|
||||||
|
/ data transfer. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_NORTC 1
|
||||||
|
#define _NORTC_MON 1
|
||||||
|
#define _NORTC_MDAY 1
|
||||||
|
#define _NORTC_YEAR 2015
|
||||||
|
/* The _FS_NORTC option switches timestamp feature. If the system does not have
|
||||||
|
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
|
||||||
|
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
|
||||||
|
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
|
||||||
|
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
|
||||||
|
/ to be added to the project to read current time form RTC. _NORTC_MON,
|
||||||
|
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
|
||||||
|
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_LOCK 0
|
||||||
|
/* The _FS_LOCK option switches file lock feature to control duplicated file open
|
||||||
|
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
|
||||||
|
/ is 1.
|
||||||
|
/
|
||||||
|
/ 0: Disable file lock feature. To avoid volume corruption, application program
|
||||||
|
/ should avoid illegal open, remove and rename to the open objects.
|
||||||
|
/ >0: Enable file lock feature. The value defines how many files/sub-directories
|
||||||
|
/ can be opened simultaneously under file lock control. Note that the file
|
||||||
|
/ lock feature is independent of re-entrancy. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _FS_REENTRANT 0
|
||||||
|
#define _FS_TIMEOUT 1000
|
||||||
|
#define _SYNC_t HANDLE
|
||||||
|
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
|
||||||
|
/ module itself. Note that regardless of this option, file access to different
|
||||||
|
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||||
|
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||||
|
/ to the same volume is under control of this feature.
|
||||||
|
/
|
||||||
|
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
|
||||||
|
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||||
|
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||||
|
/ function, must be added to the project. Samples are available in
|
||||||
|
/ option/syscall.c.
|
||||||
|
/
|
||||||
|
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||||
|
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||||
|
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||||
|
/ included somewhere in the scope of ff.c. */
|
||||||
|
|
||||||
|
|
||||||
|
#define _WORD_ACCESS 0
|
||||||
|
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||||
|
/ which access method is used to the word data on the FAT volume.
|
||||||
|
/
|
||||||
|
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||||
|
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||||
|
/
|
||||||
|
/ * Address misaligned memory access is always allowed to ALL instructions.
|
||||||
|
/ * Byte order on the memory is little-endian.
|
||||||
|
/
|
||||||
|
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
|
||||||
|
/ Following table shows allowable settings of some type of processors.
|
||||||
|
/
|
||||||
|
/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2
|
||||||
|
/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1
|
||||||
|
/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1
|
||||||
|
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
|
||||||
|
/ AVR32 0 *1 RL78 0 *2 R32C 0 *2
|
||||||
|
/ PIC18 0/1 SH-2 0 *1 M16C 0/1
|
||||||
|
/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2
|
||||||
|
/ PIC32 0 *1 H8/300H 0 *1 8051 0/1
|
||||||
|
/
|
||||||
|
/ *1:Big-endian.
|
||||||
|
/ *2:Unaligned memory access is not supported.
|
||||||
|
/ *3:Some compilers generate LDM/STM for mem_cpy function.
|
||||||
|
*/
|
||||||
|
|
33
stage2/fatfs/integer.h
Normal file
33
stage2/fatfs/integer.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*-------------------------------------------*/
|
||||||
|
/* Integer type definitions for FatFs module */
|
||||||
|
/*-------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef _FF_INTEGER
|
||||||
|
#define _FF_INTEGER
|
||||||
|
|
||||||
|
#ifdef _WIN32 /* Development platform */
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
|
||||||
|
#else /* Embedded platform */
|
||||||
|
|
||||||
|
/* This type MUST be 8-bit */
|
||||||
|
typedef unsigned char BYTE;
|
||||||
|
|
||||||
|
/* These types MUST be 16-bit */
|
||||||
|
typedef short SHORT;
|
||||||
|
typedef unsigned short WORD;
|
||||||
|
typedef unsigned short WCHAR;
|
||||||
|
|
||||||
|
/* These types MUST be 16-bit or 32-bit */
|
||||||
|
typedef int INT;
|
||||||
|
typedef unsigned int UINT;
|
||||||
|
|
||||||
|
/* These types MUST be 32-bit */
|
||||||
|
typedef long LONG;
|
||||||
|
typedef unsigned long DWORD;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
3829
stage2/fatfs/option/cc932.c
Normal file
3829
stage2/fatfs/option/cc932.c
Normal file
File diff suppressed because it is too large
Load Diff
11004
stage2/fatfs/option/cc936.c
Normal file
11004
stage2/fatfs/option/cc936.c
Normal file
File diff suppressed because it is too large
Load Diff
8634
stage2/fatfs/option/cc949.c
Normal file
8634
stage2/fatfs/option/cc949.c
Normal file
File diff suppressed because it is too large
Load Diff
6860
stage2/fatfs/option/cc950.c
Normal file
6860
stage2/fatfs/option/cc950.c
Normal file
File diff suppressed because it is too large
Load Diff
348
stage2/fatfs/option/ccsbcs.c
Normal file
348
stage2/fatfs/option/ccsbcs.c
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Unicode - Local code bidirectional converter (C)ChaN, 2015 */
|
||||||
|
/* (SBCS code pages) */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* 437 U.S.
|
||||||
|
/ 720 Arabic
|
||||||
|
/ 737 Greek
|
||||||
|
/ 771 KBL
|
||||||
|
/ 775 Baltic
|
||||||
|
/ 850 Latin 1
|
||||||
|
/ 852 Latin 2
|
||||||
|
/ 855 Cyrillic
|
||||||
|
/ 857 Turkish
|
||||||
|
/ 860 Portuguese
|
||||||
|
/ 861 Icelandic
|
||||||
|
/ 862 Hebrew
|
||||||
|
/ 863 Canadian French
|
||||||
|
/ 864 Arabic
|
||||||
|
/ 865 Nordic
|
||||||
|
/ 866 Russian
|
||||||
|
/ 869 Greek 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../ff.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if _CODE_PAGE == 437
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 720
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
|
||||||
|
0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
|
||||||
|
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
|
||||||
|
0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 737
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
|
||||||
|
0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
|
||||||
|
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
|
||||||
|
0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 771
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP771(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||||
|
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||||
|
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,
|
||||||
|
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
|
||||||
|
0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 775
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
|
||||||
|
0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
|
||||||
|
0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
|
||||||
|
0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 850
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
|
||||||
|
0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 852
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
|
||||||
|
0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
|
||||||
|
0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 855
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
|
||||||
|
0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
|
||||||
|
0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
|
||||||
|
0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
|
||||||
|
0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 857
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||||
|
0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||||
|
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
|
||||||
|
0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 860
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
|
||||||
|
0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 861
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
|
||||||
|
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 862
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||||
|
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 863
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
|
||||||
|
0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
|
||||||
|
0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 864
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
|
||||||
|
0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
|
||||||
|
0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
|
||||||
|
0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,
|
||||||
|
0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
|
||||||
|
0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,
|
||||||
|
0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,
|
||||||
|
0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 865
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||||
|
0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
|
||||||
|
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||||
|
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 866
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||||
|
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||||
|
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||||
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||||
|
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
|
||||||
|
0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif _CODE_PAGE == 869
|
||||||
|
#define _TBLDEF 1
|
||||||
|
static
|
||||||
|
const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */
|
||||||
|
0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
|
||||||
|
0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
|
||||||
|
0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
|
||||||
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,
|
||||||
|
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,
|
||||||
|
0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,
|
||||||
|
0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,
|
||||||
|
0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !_TBLDEF || !_USE_LFN
|
||||||
|
#error This file is not needed at current configuration. Remove from the project.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WCHAR ff_convert ( /* Converted character, Returns zero on error */
|
||||||
|
WCHAR chr, /* Character code to be converted */
|
||||||
|
UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
WCHAR c;
|
||||||
|
|
||||||
|
|
||||||
|
if (chr < 0x80) { /* ASCII */
|
||||||
|
c = chr;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (dir) { /* OEM code to Unicode */
|
||||||
|
c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80];
|
||||||
|
|
||||||
|
} else { /* Unicode to OEM code */
|
||||||
|
for (c = 0; c < 0x80; c++) {
|
||||||
|
if (chr == Tbl[c]) break;
|
||||||
|
}
|
||||||
|
c = (c + 0x80) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WCHAR ff_wtoupper ( /* Returns upper converted character */
|
||||||
|
WCHAR chr /* Unicode character to be upper converted */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
static const WCHAR lower[] = { /* Lower case characters to be converted */
|
||||||
|
/* Latin Supplement */ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,
|
||||||
|
/* Latin Extended-A */ 0x101,0x103,0x105,0x107,0x109,0x10B,0x10D,0x10F,0x111,0x113,0x115,0x117,0x119,0x11B,0x11D,0x11F,0x121,0x123,0x125,0x127,0x129,0x12B,0x12D,0x12F,0x131,0x133,0x135,0x137,0x13A,0x13C,0x13E,0x140,0x142,0x144,0x146,0x148,0x14B,0x14D,0x14F,0x151,0x153,0x155,0x157,0x159,0x15B,0x15D,0x15F,0x161,0x163,0x165,0x167,0x169,0x16B,0x16D,0x16F,0x171,0x173,0x175,0x177,0x17A,0x17C,0x17E,
|
||||||
|
/* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217,
|
||||||
|
/* Greek, Coptic */ 0x3B1,0x3B2,0x3B3,0x3B4,0x3B5,0x3B6,0x3B7,0x3B8,0x3B9,0x3BA,0x3BB,0x3BC,0x3BD,0x3BE,0x3BF,0x3C0,0x3C1,0x3C3,0x3C4,0x3C5,0x3C6,0x3C7,0x3C8,0x3C9,0x3CA,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB,
|
||||||
|
/* Cyrillic */ 0x430,0x431,0x432,0x433,0x434,0x435,0x436,0x437,0x438,0x439,0x43A,0x43B,0x43C,0x43D,0x43E,0x43F,0x440,0x441,0x442,0x443,0x444,0x445,0x446,0x447,0x448,0x449,0x44A,0x44B,0x44C,0x44D,0x44E,0x44F,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9,
|
||||||
|
/* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586,
|
||||||
|
/* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9,
|
||||||
|
/* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F,
|
||||||
|
/* Full-width */ 0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A
|
||||||
|
};
|
||||||
|
static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */
|
||||||
|
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x178,
|
||||||
|
0x100,0x102,0x104,0x106,0x108,0x10A,0x10C,0x10E,0x110,0x112,0x114,0x116,0x118,0x11A,0x11C,0x11E,0x120,0x122,0x124,0x126,0x128,0x12A,0x12C,0x12E,0x130,0x132,0x134,0x136,0x139,0x13B,0x13D,0x13F,0x141,0x143,0x145,0x147,0x14A,0x14C,0x14E,0x150,0x152,0x154,0x156,0x158,0x15A,0x15C,0x15E,0x160,0x162,0x164,0x166,0x168,0x16A,0x16C,0x16E,0x170,0x172,0x174,0x176,0x179,0x17B,0x17D,
|
||||||
|
0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216,
|
||||||
|
0x391,0x392,0x393,0x394,0x395,0x396,0x397,0x398,0x399,0x39A,0x39B,0x39C,0x39D,0x39E,0x39F,0x3A0,0x3A1,0x3A3,0x3A4,0x3A5,0x3A6,0x3A7,0x3A8,0x3A9,0x3AA,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA,
|
||||||
|
0x410,0x411,0x412,0x413,0x414,0x415,0x416,0x417,0x418,0x419,0x41A,0x41B,0x41C,0x41D,0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428,0x429,0x42A,0x42B,0x42C,0x42D,0x42E,0x42F,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8,
|
||||||
|
0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556,
|
||||||
|
0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8,
|
||||||
|
0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F,
|
||||||
|
0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A
|
||||||
|
};
|
||||||
|
UINT i, n, hi, li;
|
||||||
|
|
||||||
|
|
||||||
|
if (chr < 0x80) { /* ASCII characters (acceleration) */
|
||||||
|
if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20;
|
||||||
|
|
||||||
|
} else { /* Non ASCII characters (table search) */
|
||||||
|
n = 12; li = 0; hi = sizeof lower / sizeof lower[0];
|
||||||
|
do {
|
||||||
|
i = li + (hi - li) / 2;
|
||||||
|
if (chr == lower[i]) break;
|
||||||
|
if (chr > lower[i]) li = i; else hi = i;
|
||||||
|
} while (--n);
|
||||||
|
if (n) chr = upper[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return chr;
|
||||||
|
}
|
||||||
|
|
151
stage2/fatfs/option/syscall.c
Normal file
151
stage2/fatfs/option/syscall.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Sample code of OS dependent controls for FatFs */
|
||||||
|
/* (C)ChaN, 2014 */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "../ff.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if _FS_REENTRANT
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Create a Synchronization Object
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* This function is called in f_mount() function to create a new
|
||||||
|
/ synchronization object, such as semaphore and mutex. When a 0 is returned,
|
||||||
|
/ the f_mount() function fails with FR_INT_ERR.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any error */
|
||||||
|
BYTE vol, /* Corresponding logical drive being processed */
|
||||||
|
_SYNC_t *sobj /* Pointer to return the created sync object */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
|
*sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */
|
||||||
|
ret = (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
// *sobj = SyncObjects[vol]; /* uITRON (give a static created sync object) */
|
||||||
|
// ret = 1; /* The initial value of the semaphore must be 1. */
|
||||||
|
|
||||||
|
// *sobj = OSMutexCreate(0, &err); /* uC/OS-II */
|
||||||
|
// ret = (int)(err == OS_NO_ERR);
|
||||||
|
|
||||||
|
// *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */
|
||||||
|
// ret = (int)(*sobj != NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Delete a Synchronization Object */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* This function is called in f_mount() function to delete a synchronization
|
||||||
|
/ object that created with ff_cre_syncobj function. When a 0 is returned,
|
||||||
|
/ the f_mount() function fails with FR_INT_ERR.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ff_del_syncobj ( /* !=0:Function succeeded, ==0:Could not delete due to any error */
|
||||||
|
_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
|
ret = CloseHandle(sobj); /* Win32 */
|
||||||
|
|
||||||
|
// ret = 1; /* uITRON (nothing to do) */
|
||||||
|
|
||||||
|
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */
|
||||||
|
// ret = (int)(err == OS_NO_ERR);
|
||||||
|
|
||||||
|
// vSemaphoreDelete(sobj); /* FreeRTOS */
|
||||||
|
// ret = 1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Request Grant to Access the Volume */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* This function is called on entering file functions to lock the volume.
|
||||||
|
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||||
|
_SYNC_t sobj /* Sync object to wait */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */
|
||||||
|
|
||||||
|
// ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */
|
||||||
|
|
||||||
|
// OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */
|
||||||
|
// ret = (int)(err == OS_NO_ERR);
|
||||||
|
|
||||||
|
// ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Release Grant to Access the Volume */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* This function is called on leaving file functions to unlock the volume.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ff_rel_grant (
|
||||||
|
_SYNC_t sobj /* Sync object to be signaled */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ReleaseMutex(sobj); /* Win32 */
|
||||||
|
|
||||||
|
// sig_sem(sobj); /* uITRON */
|
||||||
|
|
||||||
|
// OSMutexPost(sobj); /* uC/OS-II */
|
||||||
|
|
||||||
|
// xSemaphoreGive(sobj); /* FreeRTOS */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if _USE_LFN == 3 /* LFN with a working buffer on the heap */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Allocate a memory block */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void* ff_memalloc ( /* Returns pointer to the allocated memory block */
|
||||||
|
UINT msize /* Number of bytes to allocate */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
/* Free a memory block */
|
||||||
|
/*------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void ff_memfree (
|
||||||
|
void* mblock /* Pointer to the memory block to free */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
free(mblock); /* Discard the memory block with POSIX API */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
17
stage2/fatfs/option/unicode.c
Normal file
17
stage2/fatfs/option/unicode.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "../ff.h"
|
||||||
|
|
||||||
|
#if _USE_LFN != 0
|
||||||
|
|
||||||
|
#if _CODE_PAGE == 932 /* Japanese Shift_JIS */
|
||||||
|
#include "cc932.c"
|
||||||
|
#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */
|
||||||
|
#include "cc936.c"
|
||||||
|
#elif _CODE_PAGE == 949 /* Korean */
|
||||||
|
#include "cc949.c"
|
||||||
|
#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */
|
||||||
|
#include "cc950.c"
|
||||||
|
#else /* Single Byte Character-Set */
|
||||||
|
#include "ccsbcs.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
74
stage2/gpio.h
Normal file
74
stage2/gpio.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GPIO_H__
|
||||||
|
#define __GPIO_H__
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GP_POWER = 0x00000001,
|
||||||
|
GP_SHUTDOWN = 0x00000002,
|
||||||
|
GP_FAN = 0x00000004,
|
||||||
|
GP_DCDC = 0x00000008,
|
||||||
|
GP_DISPIN = 0x00000010,
|
||||||
|
GP_SLOTLED = 0x00000020,
|
||||||
|
GP_EJECTBTN = 0x00000040,
|
||||||
|
GP_SLOTIN = 0x00000080,
|
||||||
|
GP_SENSORBAR = 0x00000100,
|
||||||
|
GP_DOEJECT = 0x00000200,
|
||||||
|
GP_EEP_CS = 0x00000400,
|
||||||
|
GP_EEP_CLK = 0x00000800,
|
||||||
|
GP_EEP_MOSI = 0x00001000,
|
||||||
|
GP_EEP_MISO = 0x00002000,
|
||||||
|
GP_AV0_SCL = 0x00004000,
|
||||||
|
GP_AV0_SDA = 0x00008000,
|
||||||
|
GP_DEBUG0 = 0x00010000,
|
||||||
|
GP_DEBUG1 = 0x00020000,
|
||||||
|
GP_DEBUG2 = 0x00040000,
|
||||||
|
GP_DEBUG3 = 0x00080000,
|
||||||
|
GP_DEBUG4 = 0x00100000,
|
||||||
|
GP_DEBUG5 = 0x00200000,
|
||||||
|
GP_DEBUG6 = 0x00400000,
|
||||||
|
GP_DEBUG7 = 0x00800000,
|
||||||
|
GP_AV1_SCL = 0x01000000,
|
||||||
|
GP_AV1_SDA = 0x02000000,
|
||||||
|
GP_MUTELAMP = 0x04000000,
|
||||||
|
GP_BT_MODE = 0x08000000,
|
||||||
|
GP_CCRH_RST = 0x10000000,
|
||||||
|
GP_WIFI_MODE = 0x20000000,
|
||||||
|
GP_SDSLOT0_PWR = 0x40000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GP2_FANSPEED = 0x00000001,
|
||||||
|
GP2_SMC_SCL = 0x00000002,
|
||||||
|
GP2_SMC_SDA = 0x00000004,
|
||||||
|
GP2_DCDC2 = 0x00000008,
|
||||||
|
GP2_AV_INT = 0x00000010,
|
||||||
|
GP2_CCRIO12 = 0x00000020,
|
||||||
|
GP2_AV_RST = 0x00000040,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GP_DEBUG_SHIFT 16
|
||||||
|
#define GP_DEBUG_MASK 0xFF0000
|
||||||
|
|
||||||
|
#define GP_ALL 0xFFFFFF
|
||||||
|
#define GP_OWNER_PPC (GP_AVE_SDA | GP_AVE_SCL | GP_DOEJECT | GP_SENSORBAR | GP_SLOTIN | GP_SLOTLED)
|
||||||
|
#define GP_OWNER_ARM (GP_ALL ^ GP_OWNER_PPC)
|
||||||
|
#define GP_INPUTS (GP_POWER | GP_EJECTBTN | GP_SLOTIN | GP_EEP_MISO | GP_AVE_SDA)
|
||||||
|
#define GP_OUTPUTS (GP_ALL ^ GP_INPUTS)
|
||||||
|
#define GP_ARM_INPUTS (GP_INPUTS & GP_OWNER_ARM)
|
||||||
|
#define GP_PPC_INPUTS (GP_INPUTS & GP_OWNER_PPC)
|
||||||
|
#define GP_ARM_OUTPUTS (GP_OUTPUTS & GP_OWNER_ARM)
|
||||||
|
#define GP_PPC_OUTPUTS (GP_OUTPUTS & GP_OWNER_PPC)
|
||||||
|
#define GP_DEFAULT_ON (GP_AVE_SCL | GP_DCDC | GP_FAN)
|
||||||
|
#define GP_ARM_DEFAULT_ON (GP_DEFAULT_ON & GP_OWNER_ARM)
|
||||||
|
#define GP_PPC_DEFAULT_ON (GP_DEFAULT_ON & GP_OWNER_PPC)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
46
stage2/hmac.c
Normal file
46
stage2/hmac.c
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "hmac.h"
|
||||||
|
#include "sha.h"
|
||||||
|
#include "hmac.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#define HMAC_IPAD 0x36
|
||||||
|
#define HMAC_OPAD 0x5C
|
||||||
|
|
||||||
|
void hmac_init(hmac_ctx* ctx, const u8* key, int size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(ctx->key, 0, sizeof(ctx->key));
|
||||||
|
|
||||||
|
if (size > sizeof(ctx->key))
|
||||||
|
sha_hash(key, ctx->key, size);
|
||||||
|
else
|
||||||
|
memcpy(ctx->key, key, size);
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(ctx->key); i++)
|
||||||
|
ctx->key[i] ^= HMAC_IPAD;
|
||||||
|
|
||||||
|
sha_init(&ctx->hash_ctx);
|
||||||
|
sha_update(&ctx->hash_ctx, ctx->key, sizeof(ctx->key));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmac_update(hmac_ctx* ctx, const void* data, int size)
|
||||||
|
{
|
||||||
|
sha_update(&ctx->hash_ctx, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hmac_final(hmac_ctx* ctx, u8* hmac)
|
||||||
|
{
|
||||||
|
u8 hash[SHA_HASH_SIZE];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sha_final(&ctx->hash_ctx, hash);
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(ctx->key); i++)
|
||||||
|
ctx->key[i] ^= HMAC_IPAD ^ HMAC_OPAD;
|
||||||
|
|
||||||
|
sha_init(&ctx->hash_ctx);
|
||||||
|
sha_update(&ctx->hash_ctx, ctx->key, sizeof(ctx->key));
|
||||||
|
sha_update(&ctx->hash_ctx, hash, sizeof(hash));
|
||||||
|
sha_final(&ctx->hash_ctx, hmac);
|
||||||
|
}
|
18
stage2/hmac.h
Normal file
18
stage2/hmac.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _HMAC_H
|
||||||
|
#define _HMAC_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "sha.h"
|
||||||
|
|
||||||
|
#define HMAC_SIZE (SHA_HASH_SIZE)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 key[SHA_BLOCK_SIZE];
|
||||||
|
sha_ctx hash_ctx;
|
||||||
|
} hmac_ctx;
|
||||||
|
|
||||||
|
void hmac_init(hmac_ctx* ctx, const u8* key, int size);
|
||||||
|
void hmac_update(hmac_ctx* ctx, const void* data, int size);
|
||||||
|
void hmac_final(hmac_ctx *ctx, u8 *hmac);
|
||||||
|
|
||||||
|
#endif /* _HMAC_H */
|
158
stage2/irq.c
Normal file
158
stage2/irq.c
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (C) 2009 Andre Heider "dhewg" <dhewg@wiibrew.org>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "irq.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "nand.h"
|
||||||
|
#include "sdcard.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
static u32 _alarm_frequency = 0;
|
||||||
|
|
||||||
|
void irq_setup_stack(void);
|
||||||
|
|
||||||
|
void irq_initialize(void)
|
||||||
|
{
|
||||||
|
irq_setup_stack();
|
||||||
|
write32(LT_INTMR_AHBALL_ARM, 0);
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, 0xffffffff);
|
||||||
|
write32(LT_INTMR_AHBLT_ARM, 0);
|
||||||
|
write32(LT_INTSR_AHBLT_ARM, 0xffffffff);
|
||||||
|
irq_restore(CPSR_FIQDIS);
|
||||||
|
|
||||||
|
//???
|
||||||
|
write32(LT_INTMR_AHBALL_ARM2X, 0);
|
||||||
|
write32(LT_INTMR_AHBLT_ARM2X, 0);
|
||||||
|
write32(LT_ERROR_MASK, 0);
|
||||||
|
|
||||||
|
write32(LT_ALARM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_shutdown(void)
|
||||||
|
{
|
||||||
|
write32(LT_INTMR_AHBALL_ARM, 0);
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, 0xffffffff);
|
||||||
|
write32(LT_INTMR_AHBLT_ARM, 0);
|
||||||
|
write32(LT_INTSR_AHBLT_ARM, 0xffffffff);
|
||||||
|
irq_kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_handler(void)
|
||||||
|
{
|
||||||
|
u32 all_enabled = read32(LT_INTMR_AHBALL_ARM);
|
||||||
|
u32 all_flags = read32(LT_INTSR_AHBALL_ARM);
|
||||||
|
u32 all_mask = all_enabled & all_flags;
|
||||||
|
|
||||||
|
u32 lt_enabled = read32(LT_INTMR_AHBLT_ARM);
|
||||||
|
u32 lt_flags = read32(LT_INTSR_AHBLT_ARM);
|
||||||
|
u32 lt_mask = lt_enabled & lt_flags;
|
||||||
|
|
||||||
|
/*DEBUG("In IRQ handler: ALL (0x%08x 0x%08x 0x%08x) LT (0x%08x 0x%08x 0x%08x)\n",
|
||||||
|
all_enabled, all_flags, all_mask, lt_enabled, lt_flags, lt_mask);*/
|
||||||
|
|
||||||
|
if(all_mask & IRQF_TIMER) {
|
||||||
|
if (_alarm_frequency)
|
||||||
|
write32(LT_ALARM, read32(LT_TIMER) + _alarm_frequency);
|
||||||
|
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(all_mask & IRQF_NAND) {
|
||||||
|
// DEBUG("IRQ: NAND\n");
|
||||||
|
write32(NAND_CTRL, 0x7fffffff); // shut it up
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_NAND);
|
||||||
|
nand_irq();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if(all_mask & IRQF_GPIO1B) {
|
||||||
|
// DEBUG("IRQ: GPIO1B\n");
|
||||||
|
write32(HW_GPIO1BINTFLAG, 0xFFFFFF); // shut it up
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_GPIO1B);
|
||||||
|
}
|
||||||
|
if(all_mask & IRQF_GPIO1) {
|
||||||
|
// DEBUG("IRQ: GPIO1\n");
|
||||||
|
write32(HW_GPIO1INTFLAG, 0xFFFFFF); // shut it up
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_GPIO1);
|
||||||
|
}
|
||||||
|
if(all_mask & IRQF_RESET) {
|
||||||
|
// DEBUG("IRQ: RESET\n");
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_RESET);
|
||||||
|
}*/
|
||||||
|
if(all_mask & IRQF_SHA1) {
|
||||||
|
// DEBUG("IRQ: SHA1\n");
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_SHA1);
|
||||||
|
}
|
||||||
|
if(all_mask & IRQF_AES) {
|
||||||
|
// DEBUG("IRQ: AES\n");
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_AES);
|
||||||
|
}
|
||||||
|
if(all_mask & IRQF_SD0) {
|
||||||
|
// DEBUG("IRQ: SD0\n");
|
||||||
|
sdcard_irq();
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, IRQF_SD0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lt_mask & IRQLF_SD2) {
|
||||||
|
// DEBUG("IRQL: SD2\n");
|
||||||
|
//mlc_irq(); //Ash: don't need MLC support, turning it off
|
||||||
|
write32(LT_INTSR_AHBLT_ARM, IRQLF_SD2);
|
||||||
|
}
|
||||||
|
|
||||||
|
all_mask &= ~IRQF_ALL;
|
||||||
|
if(all_mask) {
|
||||||
|
DEBUG("IRQ: ALL unknown 0x%08lx\n", all_mask);
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, all_mask);
|
||||||
|
}
|
||||||
|
lt_mask &= ~IRQLF_ALL;
|
||||||
|
if(lt_mask) {
|
||||||
|
DEBUG("IRQ: LT unknown 0x%08lx\n", lt_mask);
|
||||||
|
write32(LT_INTSR_AHBALL_ARM, lt_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_enable(u32 irq)
|
||||||
|
{
|
||||||
|
set32(LT_INTMR_AHBALL_ARM, 1<<irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irql_enable(u32 irq)
|
||||||
|
{
|
||||||
|
set32(LT_INTMR_AHBLT_ARM, 1<<irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_disable(u32 irq)
|
||||||
|
{
|
||||||
|
clear32(LT_INTMR_AHBALL_ARM, 1<<irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irql_disable(u32 irq)
|
||||||
|
{
|
||||||
|
clear32(LT_INTMR_AHBLT_ARM, 1<<irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_set_alarm(u32 ms, u8 enable)
|
||||||
|
{
|
||||||
|
_alarm_frequency = IRQ_ALARM_MS2REG(ms);
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
write32(LT_ALARM, read32(LT_TIMER) + _alarm_frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_wait(void)
|
||||||
|
{
|
||||||
|
u32 data = 0;
|
||||||
|
__asm__ volatile ( "mcr p15, 0, %0, c7, c0, 4" : : "r" (data) );
|
||||||
|
}
|
75
stage2/irq.h
Normal file
75
stage2/irq.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define IRQ_TIMER 0
|
||||||
|
#define IRQ_NAND 1
|
||||||
|
#define IRQ_AES 2
|
||||||
|
#define IRQ_SHA1 3
|
||||||
|
#define IRQ_EHCI 4
|
||||||
|
#define IRQ_OHCI0 5
|
||||||
|
#define IRQ_OHCI1 6
|
||||||
|
#define IRQ_SD0 7
|
||||||
|
#define IRQ_WIFI 8
|
||||||
|
#define IRQ_GPIO1B 10
|
||||||
|
#define IRQ_GPIO1 11
|
||||||
|
#define IRQ_RESET 17
|
||||||
|
#define IRQ_PPCIPC 30
|
||||||
|
#define IRQ_IPC 31
|
||||||
|
|
||||||
|
#define IRQL_SD2 0
|
||||||
|
|
||||||
|
#define IRQF_TIMER (1<<IRQ_TIMER)
|
||||||
|
#define IRQF_NAND (1<<IRQ_NAND)
|
||||||
|
#define IRQF_AES (1<<IRQ_AES)
|
||||||
|
#define IRQF_SHA1 (1<<IRQ_SHA1)
|
||||||
|
#define IRQF_SD0 (1<<IRQ_SD0)
|
||||||
|
#define IRQF_GPIO1B (1<<IRQ_GPIO1B)
|
||||||
|
#define IRQF_GPIO1 (1<<IRQ_GPIO1)
|
||||||
|
#define IRQF_RESET (1<<IRQ_RESET)
|
||||||
|
#define IRQF_IPC (1<<IRQ_IPC)
|
||||||
|
|
||||||
|
#define IRQLF_SD2 (1<<IRQL_SD2)
|
||||||
|
|
||||||
|
#define IRQF_ALL ( \
|
||||||
|
IRQF_TIMER|IRQF_NAND|IRQF_GPIO1B|IRQF_GPIO1| \
|
||||||
|
IRQF_RESET|IRQF_IPC|IRQF_AES|IRQF_SHA1|IRQF_SD0 \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define IRQLF_ALL ( \
|
||||||
|
IRQLF_SD2 \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define CPSR_IRQDIS 0x80
|
||||||
|
#define CPSR_FIQDIS 0x40
|
||||||
|
|
||||||
|
#define IRQ_ALARM_MS2REG(x) (1898 * x)
|
||||||
|
|
||||||
|
void irq_initialize(void);
|
||||||
|
void irq_shutdown(void);
|
||||||
|
|
||||||
|
void irq_enable(u32 irq);
|
||||||
|
void irq_disable(u32 irq);
|
||||||
|
|
||||||
|
void irql_enable(u32 irq);
|
||||||
|
void irql_disable(u32 irq);
|
||||||
|
|
||||||
|
u32 irq_kill(void);
|
||||||
|
void irq_restore(u32 cookie);
|
||||||
|
|
||||||
|
void irq_wait(void);
|
||||||
|
|
||||||
|
void irq_set_alarm(u32 ms, u8 enable);
|
56
stage2/irq_asm.S
Normal file
56
stage2/irq_asm.S
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "latte.h"
|
||||||
|
//#include "irq.h"
|
||||||
|
|
||||||
|
.globl v_irq
|
||||||
|
.globl irq_setup_stack
|
||||||
|
.globl irq_kill
|
||||||
|
.globl irq_restore
|
||||||
|
.extern __irqstack_addr
|
||||||
|
.extern irq_handler
|
||||||
|
|
||||||
|
irq_setup_stack:
|
||||||
|
@ Switch to IRQ mode
|
||||||
|
mrs r0, cpsr
|
||||||
|
msr cpsr_c, #0xd2
|
||||||
|
|
||||||
|
@ Setup interrupt stack
|
||||||
|
ldr sp, =__irqstack_addr
|
||||||
|
|
||||||
|
@ Restore mode
|
||||||
|
msr cpsr_c, r0
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
v_irq:
|
||||||
|
push {r0-r3, r9, r12, lr}
|
||||||
|
|
||||||
|
blx irq_handler
|
||||||
|
|
||||||
|
pop {r0-r3, r9, r12, lr}
|
||||||
|
subs pc, lr, #4
|
||||||
|
|
||||||
|
irq_kill:
|
||||||
|
mrs r1, cpsr
|
||||||
|
and r0, r1, #(0x80|0x40)
|
||||||
|
orr r1, r1, #(0x80|0x40)
|
||||||
|
msr cpsr_c, r1
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
irq_restore:
|
||||||
|
mrs r1, cpsr
|
||||||
|
bic r1, r1, #(0x80|0x40)
|
||||||
|
orr r1, r1, r0
|
||||||
|
msr cpsr_c, r1
|
||||||
|
bx lr
|
||||||
|
|
22
stage2/isfs/hmac_seed.h
Normal file
22
stage2/isfs/hmac_seed.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include "sha.h"
|
||||||
|
#include "hmac.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u16 x1;
|
||||||
|
u16 uid;
|
||||||
|
char name[0x0C];
|
||||||
|
u32 iblk;
|
||||||
|
u32 ifst;
|
||||||
|
u32 x3;
|
||||||
|
u8 pad0[0x24];
|
||||||
|
} isfs_hmac_data;
|
||||||
|
_Static_assert(sizeof(isfs_hmac_data) == 0x40, "isfs_hmac_data size must be 0x40!");
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 pad0[0x12];
|
||||||
|
u16 cluster;
|
||||||
|
u8 pad1[0x2b];
|
||||||
|
} isfs_hmac_meta;
|
||||||
|
_Static_assert(sizeof(isfs_hmac_meta) == 0x40, "isfs_hmac_meta size must be 0x40!");
|
240
stage2/isfs/isfs.c
Normal file
240
stage2/isfs/isfs.c
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "nand.h"
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
#include "isfs/volume.h"
|
||||||
|
#include "isfs/super.h"
|
||||||
|
#include "isfs/isfshax.h"
|
||||||
|
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
int isfs_init(void)
|
||||||
|
{
|
||||||
|
isfs_ctx *ctx;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if(initialized) return res;
|
||||||
|
|
||||||
|
ctx = isfs_get_volume(ISFSVOL_SLC);
|
||||||
|
ctx->mounted = !isfs_load_super(ctx, 0, ISFSHAX_GENERATION_FIRST);
|
||||||
|
|
||||||
|
/*ctx = isfs_get_volume(ISFSVOL_SLCCMPT);
|
||||||
|
ctx->mounted = !isfs_load_super(ctx, 0, 0xffffffff);*/
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_fini(void)
|
||||||
|
{
|
||||||
|
if(!initialized) return 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < isfs_num_volumes(); i++)
|
||||||
|
isfs_get_volume(i)->mounted = false;
|
||||||
|
|
||||||
|
initialized = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isfs_fst* isfs_stat(const char* path)
|
||||||
|
{
|
||||||
|
isfs_ctx* ctx = NULL;
|
||||||
|
path = isfs_do_volume(path, &ctx);
|
||||||
|
if(!ctx || !path) return NULL;
|
||||||
|
|
||||||
|
return isfs_find_fst(ctx, NULL, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_open(isfs_file* file, const char* path)
|
||||||
|
{
|
||||||
|
if(!file || !path) return -1;
|
||||||
|
|
||||||
|
isfs_ctx* ctx = NULL;
|
||||||
|
path = isfs_do_volume(path, &ctx);
|
||||||
|
if(!ctx) return -2;
|
||||||
|
|
||||||
|
isfs_fst* fst = isfs_find_fst(ctx, NULL, path);
|
||||||
|
if(!fst) return -3;
|
||||||
|
|
||||||
|
if(!isfs_fst_is_file(fst)) return -4;
|
||||||
|
|
||||||
|
memset(file, 0, sizeof(isfs_file));
|
||||||
|
file->volume = ctx->volume;
|
||||||
|
file->fst = fst;
|
||||||
|
|
||||||
|
file->cluster = fst->sub;
|
||||||
|
file->offset = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_close(isfs_file* file)
|
||||||
|
{
|
||||||
|
if(!file) return -1;
|
||||||
|
memset(file, 0, sizeof(isfs_file));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_seek(isfs_file* file, s32 offset, int whence)
|
||||||
|
{
|
||||||
|
if(!file) return -1;
|
||||||
|
|
||||||
|
isfs_ctx* ctx = isfs_get_volume(file->volume);
|
||||||
|
isfs_fst* fst = file->fst;
|
||||||
|
if(!ctx || !fst) return -2;
|
||||||
|
|
||||||
|
switch(whence) {
|
||||||
|
case SEEK_SET:
|
||||||
|
if(offset < 0) return -1;
|
||||||
|
if(offset > fst->size) return -1;
|
||||||
|
file->offset = offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEEK_CUR:
|
||||||
|
if(file->offset + offset > fst->size) return -1;
|
||||||
|
if(offset + fst->size < 0) return -1;
|
||||||
|
file->offset += offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEEK_END:
|
||||||
|
if(file->offset + offset > fst->size) return -1;
|
||||||
|
if(offset + fst->size < 0) return -1;
|
||||||
|
file->offset = fst->size + offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 sub = fst->sub;
|
||||||
|
size_t size = file->offset;
|
||||||
|
|
||||||
|
while(size > 8 * PAGE_SIZE) {
|
||||||
|
sub = isfs_get_fat(ctx)[sub];
|
||||||
|
size -= 8 * PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->cluster = sub;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_read(isfs_file* file, void* buffer, size_t size, size_t* bytes_read)
|
||||||
|
{
|
||||||
|
if(!file || !buffer) return -1;
|
||||||
|
|
||||||
|
isfs_ctx* ctx = isfs_get_volume(file->volume);
|
||||||
|
isfs_fst* fst = file->fst;
|
||||||
|
if(!ctx || !fst) return -2;
|
||||||
|
|
||||||
|
if(size + file->offset > fst->size)
|
||||||
|
size = fst->size - file->offset;
|
||||||
|
|
||||||
|
size_t total = size;
|
||||||
|
|
||||||
|
void* cluster_buf = memalign(64, CLUSTER_SIZE);
|
||||||
|
if(!cluster_buf) return -3;
|
||||||
|
|
||||||
|
while(size) {
|
||||||
|
size_t pos = file->offset % CLUSTER_SIZE;
|
||||||
|
size_t copy = CLUSTER_SIZE - pos;
|
||||||
|
if(copy > size) copy = size;
|
||||||
|
|
||||||
|
if (isfs_read_volume(ctx, file->cluster, 1, ISFSVOL_FLAG_ENCRYPTED, NULL, cluster_buf) < 0)
|
||||||
|
{
|
||||||
|
free(cluster_buf);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
memcpy(buffer, cluster_buf + pos, copy);
|
||||||
|
|
||||||
|
file->offset += copy;
|
||||||
|
buffer += copy;
|
||||||
|
size -= copy;
|
||||||
|
|
||||||
|
if((pos + copy) >= CLUSTER_SIZE)
|
||||||
|
file->cluster = isfs_get_fat(ctx)[file->cluster];
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cluster_buf);
|
||||||
|
|
||||||
|
*bytes_read = total;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_diropen(isfs_dir* dir, const char* path)
|
||||||
|
{
|
||||||
|
if(!dir || !path) return -1;
|
||||||
|
|
||||||
|
isfs_ctx* ctx = NULL;
|
||||||
|
path = isfs_do_volume(path, &ctx);
|
||||||
|
if(!ctx) return -2;
|
||||||
|
|
||||||
|
isfs_fst* fst = isfs_find_fst(ctx, NULL, path);
|
||||||
|
if(!fst) return -3;
|
||||||
|
|
||||||
|
if(!isfs_fst_is_dir(fst)) return -4;
|
||||||
|
if(fst->sub == 0xFFFF) return -2;
|
||||||
|
|
||||||
|
isfs_fst* root = isfs_get_fst(ctx);
|
||||||
|
|
||||||
|
memset(dir, 0, sizeof(isfs_dir));
|
||||||
|
dir->volume = ctx->volume;
|
||||||
|
dir->dir = fst;
|
||||||
|
dir->child = &root[fst->sub];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_dirread(isfs_dir* dir, isfs_fst** info)
|
||||||
|
{
|
||||||
|
if(!dir) return -1;
|
||||||
|
|
||||||
|
isfs_ctx* ctx = isfs_get_volume(dir->volume);
|
||||||
|
isfs_fst* fst = dir->dir;
|
||||||
|
if(!ctx || !fst) return -2;
|
||||||
|
|
||||||
|
isfs_fst* root = isfs_get_fst(ctx);
|
||||||
|
|
||||||
|
if(!info) {
|
||||||
|
dir->child = &root[fst->sub];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*info = dir->child;
|
||||||
|
|
||||||
|
if(dir->child != NULL) {
|
||||||
|
if(dir->child->sib == 0xFFFF)
|
||||||
|
dir->child = NULL;
|
||||||
|
else
|
||||||
|
dir->child = &root[dir->child->sib];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_dirreset(isfs_dir* dir)
|
||||||
|
{
|
||||||
|
return isfs_dirread(dir, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_dirclose(isfs_dir* dir)
|
||||||
|
{
|
||||||
|
if(!dir) return -1;
|
||||||
|
memset(dir, 0, sizeof(isfs_dir));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
69
stage2/isfs/isfs.h
Normal file
69
stage2/isfs/isfs.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ISFS_H
|
||||||
|
#define _ISFS_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "super.h"
|
||||||
|
|
||||||
|
typedef struct isfs_ctx {
|
||||||
|
int volume;
|
||||||
|
const char name[0x10];
|
||||||
|
const u32 bank;
|
||||||
|
const u32 super_count;
|
||||||
|
int index;
|
||||||
|
u8* const super;
|
||||||
|
u32 generation;
|
||||||
|
u32 version;
|
||||||
|
bool mounted;
|
||||||
|
void* key;
|
||||||
|
void* hmac;
|
||||||
|
devoptab_t devoptab;
|
||||||
|
} isfs_ctx;
|
||||||
|
|
||||||
|
typedef struct isfs_file {
|
||||||
|
int volume;
|
||||||
|
isfs_fst* fst;
|
||||||
|
size_t offset;
|
||||||
|
u16 cluster;
|
||||||
|
} isfs_file;
|
||||||
|
|
||||||
|
typedef struct isfs_dir {
|
||||||
|
int volume;
|
||||||
|
isfs_fst* dir;
|
||||||
|
isfs_fst* child;
|
||||||
|
} isfs_dir;
|
||||||
|
|
||||||
|
int isfs_init(void);
|
||||||
|
int isfs_fini(void);
|
||||||
|
|
||||||
|
isfs_fst* isfs_stat(const char* path);
|
||||||
|
|
||||||
|
int isfs_open(isfs_file* file, const char* path);
|
||||||
|
int isfs_close(isfs_file* file);
|
||||||
|
|
||||||
|
int isfs_seek(isfs_file* file, s32 offset, int whence);
|
||||||
|
int isfs_read(isfs_file* file, void* buffer, size_t size, size_t* bytes_read);
|
||||||
|
|
||||||
|
int isfs_diropen(isfs_dir* dir, const char* path);
|
||||||
|
int isfs_dirread(isfs_dir* dir, isfs_fst** info);
|
||||||
|
int isfs_dirreset(isfs_dir* dir);
|
||||||
|
int isfs_dirclose(isfs_dir* dir);
|
||||||
|
|
||||||
|
#ifdef ISFS_DEBUG
|
||||||
|
# define ISFS_debug(f, arg...) DEBUG("ISFS: " f, ##arg);
|
||||||
|
#else
|
||||||
|
# define ISFS_debug(f, arg...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
135
stage2/isfs/isfshax.c
Normal file
135
stage2/isfs/isfshax.c
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* isfshax.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 rw-r-r-0644 <rwrr0644@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* isfshax is installed to 4 superblock slots for redundancy
|
||||||
|
* against ecc errors and nand blocks wear out.
|
||||||
|
*
|
||||||
|
* when a boot1 superblock recommit attempt (due to ecc errors)
|
||||||
|
* is detected, the superblock will be rewritten to the next
|
||||||
|
* isfshax slot after correcting ecc errors.
|
||||||
|
*
|
||||||
|
* the generation number is incremented after each correction;
|
||||||
|
* once the maximum allowed genertion number is reached, all
|
||||||
|
* isfshax superblocks are rewritten with a lower generation number.
|
||||||
|
*
|
||||||
|
* if one of the blocks becomes bad during the rewrite,
|
||||||
|
* the range of used generations numbers is incremented to ensure
|
||||||
|
* the old superblock will not be used:
|
||||||
|
* 0 BAD BLOCKS -> generation no. 0xfffffaff-0xfffffbff
|
||||||
|
* ...
|
||||||
|
* 3 BAD BLOCKS -> generation no. 0xfffffdff-0xfffffeff
|
||||||
|
*
|
||||||
|
* bad block information is stored along with other informations in a
|
||||||
|
* separate isfshax info structure inside of the superblock, since all
|
||||||
|
* isfshax slots are already marked as bad to guard against IOSU intervention
|
||||||
|
* inside of the normal ISFS cluster allocation table.
|
||||||
|
*/
|
||||||
|
#include "types.h"
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
#include "isfs/super.h"
|
||||||
|
#include "isfs/volume.h"
|
||||||
|
#include "isfs/isfshax.h"
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* boot1 superblock loading address.
|
||||||
|
* this can be used to read the current generation
|
||||||
|
* and the list of isfshax superblock slots, but it
|
||||||
|
* can't be rewritten directly to nand as it has been
|
||||||
|
* modified by the stage1 payload. */
|
||||||
|
static const isfshax_super *
|
||||||
|
boot1_superblock = (const isfshax_super *)(0x01f80000);
|
||||||
|
|
||||||
|
static isfshax_super
|
||||||
|
superblock;
|
||||||
|
|
||||||
|
|
||||||
|
int isfshax_refresh()
|
||||||
|
{
|
||||||
|
isfs_ctx *slc = isfs_get_volume(ISFSVOL_SLC);
|
||||||
|
u32 curindex, offs, count = 1, written = 0;
|
||||||
|
u32 generation;
|
||||||
|
|
||||||
|
/* detect if the superblock contains ecc errors and boot1
|
||||||
|
* attempted to recommit the superblock */
|
||||||
|
if (boot1_superblock->generation == boot1_superblock->isfshax.generation)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* load the newest valid isfshax superblock slot */
|
||||||
|
curindex = boot1_superblock->isfshax.index;
|
||||||
|
for (offs = 0; offs < ISFSHAX_REDUNDANCY; offs++) {
|
||||||
|
u32 index = (curindex + offs) & (ISFSHAX_REDUNDANCY - 1);
|
||||||
|
u32 slot = boot1_superblock->isfshax.slots[index] & ~ISFSHAX_BAD_SLOT;
|
||||||
|
|
||||||
|
if (isfs_read_super(slc, &superblock, slot) >= 0) {
|
||||||
|
curindex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (offs == ISFSHAX_REDUNDANCY)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
/* if the last valid generation is reached, rewrite/erase all
|
||||||
|
* isfshax superblocks with a lower generation number */
|
||||||
|
generation = superblock.generation + 1;
|
||||||
|
if (generation >= (superblock.isfshax.generationbase + ISFSHAX_GENERATION_RANGE)) {
|
||||||
|
generation = superblock.isfshax.generationbase;
|
||||||
|
count = ISFSHAX_REDUNDANCY;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (offs = 1; (offs <= ISFSHAX_REDUNDANCY) && (written < count); offs++) {
|
||||||
|
u32 index = (curindex + offs) & (ISFSHAX_REDUNDANCY - 1);
|
||||||
|
u32 slot = superblock.isfshax.slots[index] & ~ISFSHAX_BAD_SLOT;
|
||||||
|
|
||||||
|
/* skip slots that became bad after a superblock rewrite */
|
||||||
|
if (superblock.isfshax.slots[index] & ISFSHAX_BAD_SLOT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* if the slot currently in use is being rewritten, ensure
|
||||||
|
* at least another slot was already successfully written */
|
||||||
|
if ((index == curindex) && !written)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* update superblock informations */
|
||||||
|
superblock.isfshax.index = index;
|
||||||
|
superblock.isfshax.generation = generation;
|
||||||
|
superblock.generation = generation;
|
||||||
|
|
||||||
|
/* rewrite and verify the superblock */
|
||||||
|
if (isfs_write_super(slc, &superblock, slot) >= 0)
|
||||||
|
{
|
||||||
|
generation++;
|
||||||
|
written++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* block became bad during write operation, mark
|
||||||
|
* the block as bad and go to next generation range */
|
||||||
|
superblock.isfshax.slots[index] |= ISFSHAX_BAD_SLOT;
|
||||||
|
superblock.isfshax.generationbase += ISFSHAX_GENERATION_RANGE;
|
||||||
|
generation = superblock.isfshax.generationbase;
|
||||||
|
|
||||||
|
/* the current superblock became bad. ensure the other
|
||||||
|
* isfshax superblock are updated with the new generation range
|
||||||
|
* and bad slot information. */
|
||||||
|
if (index == curindex)
|
||||||
|
{
|
||||||
|
offs = 1;
|
||||||
|
written = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all isfshax superblocks became bad, or the nand writing code stopped
|
||||||
|
* working correctly. either way the user should probably be informed. */
|
||||||
|
if (!written)
|
||||||
|
return -3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
47
stage2/isfs/isfshax.h
Normal file
47
stage2/isfs/isfshax.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* isfshax.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 rw-r-r-0644 <rwrr0644@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include "nand.h"
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
|
||||||
|
#define ISFSHAX_MAGIC 0x48415858
|
||||||
|
|
||||||
|
#define ISFSHAX_REDUNDANCY (1 << 2)
|
||||||
|
|
||||||
|
#define ISFSHAX_GENERATION_FIRST 0xffff7fff
|
||||||
|
#define ISFSHAX_GENERATION_RANGE 0x100
|
||||||
|
|
||||||
|
#define ISFSHAX_BAD_SLOT 0x80
|
||||||
|
|
||||||
|
typedef struct isfshax_info
|
||||||
|
{
|
||||||
|
u32 magic;
|
||||||
|
u8 slots[ISFSHAX_REDUNDANCY];
|
||||||
|
u32 generation;
|
||||||
|
u32 generationbase;
|
||||||
|
u32 index;
|
||||||
|
} isfshax_info;
|
||||||
|
_Static_assert(sizeof(isfshax_info) == 0x14, "isfshax_info must be 0x14");
|
||||||
|
|
||||||
|
typedef struct isfshax_super
|
||||||
|
{
|
||||||
|
char magic[4];
|
||||||
|
u32 generation;
|
||||||
|
u32 x1;
|
||||||
|
u16 fat[CLUSTER_COUNT];
|
||||||
|
isfs_fst fst[6143];
|
||||||
|
isfshax_info isfshax;
|
||||||
|
} PACKED ALIGNED(64) isfshax_super;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(isfshax_super) == ISFSSUPER_SIZE, "isfshax_super must be 0x40000");
|
||||||
|
|
||||||
|
|
||||||
|
int isfshax_refresh();
|
||||||
|
|
251
stage2/isfs/super.c
Normal file
251
stage2/isfs/super.c
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "nand.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
#include "isfs/volume.h"
|
||||||
|
#include "isfs/hmac_seed.h"
|
||||||
|
#include "isfs/super.h"
|
||||||
|
#include "isfs/isfshax.h"
|
||||||
|
|
||||||
|
int isfs_get_super_version(void* buffer)
|
||||||
|
{
|
||||||
|
if(!memcmp(buffer, "SFFS", 4)) return 0;
|
||||||
|
if(!memcmp(buffer, "SFS!", 4)) return 1;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 isfs_get_super_generation(void* buffer)
|
||||||
|
{
|
||||||
|
return read32((u32)buffer + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
isfs_hdr* isfs_get_hdr(isfs_ctx* ctx)
|
||||||
|
{
|
||||||
|
return (isfs_hdr*)&ctx->super[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
u16* isfs_get_fat(isfs_ctx* ctx)
|
||||||
|
{
|
||||||
|
return (u16*)&ctx->super[0x0C];
|
||||||
|
}
|
||||||
|
|
||||||
|
isfs_fst* isfs_get_fst(isfs_ctx* ctx)
|
||||||
|
{
|
||||||
|
return (isfs_fst*)&ctx->super[0x10000 + 0x0C];
|
||||||
|
}
|
||||||
|
|
||||||
|
static isfs_fst* isfs_check_file(isfs_ctx* ctx, isfs_fst* fst, const char* path)
|
||||||
|
{
|
||||||
|
char fst_name[sizeof(fst->name) + 1] = {0};
|
||||||
|
memcpy(fst_name, fst->name, sizeof(fst->name));
|
||||||
|
|
||||||
|
//ISFS_debug("file: %s vs %s\n", path, fst_name);
|
||||||
|
|
||||||
|
if(!strcmp(fst_name, path))
|
||||||
|
return fst;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isfs_fst* isfs_check_dir(isfs_ctx* ctx, isfs_fst* fst, const char* path)
|
||||||
|
{
|
||||||
|
isfs_fst* root = isfs_get_fst(ctx);
|
||||||
|
|
||||||
|
size_t size = strlen(path);
|
||||||
|
const char* remaining = strchr(path, '/');
|
||||||
|
if(remaining) size = remaining - path;
|
||||||
|
|
||||||
|
if(size > sizeof(fst->name)) return NULL;
|
||||||
|
|
||||||
|
char name[sizeof(fst->name) + 1] = {0};
|
||||||
|
memcpy(name, path, size);
|
||||||
|
|
||||||
|
char fst_name[sizeof(fst->name) + 1] = {0};
|
||||||
|
memcpy(fst_name, fst->name, sizeof(fst->name));
|
||||||
|
|
||||||
|
if(size == 0 || !strcmp(name, fst_name))
|
||||||
|
{
|
||||||
|
if(fst->sub != 0xFFFF && remaining != NULL && remaining[1] != '\0')
|
||||||
|
{
|
||||||
|
while(*remaining == '/') remaining++;
|
||||||
|
return isfs_find_fst(ctx, &root[fst->sub], remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fst;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_fst_get_type(const isfs_fst* fst)
|
||||||
|
{
|
||||||
|
return fst->mode & 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isfs_fst_is_file(const isfs_fst* fst)
|
||||||
|
{
|
||||||
|
return isfs_fst_get_type(fst) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isfs_fst_is_dir(const isfs_fst* fst)
|
||||||
|
{
|
||||||
|
return isfs_fst_get_type(fst) == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
isfs_fst* isfs_find_fst(isfs_ctx* ctx, isfs_fst* fst, const char* path)
|
||||||
|
{
|
||||||
|
isfs_fst* root = isfs_get_fst(ctx);
|
||||||
|
if(!fst) fst = root;
|
||||||
|
|
||||||
|
if(fst->sib != 0xFFFF) {
|
||||||
|
isfs_fst* result = isfs_find_fst(ctx, &root[fst->sib], path);
|
||||||
|
if(result) return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(isfs_fst_get_type(fst)) {
|
||||||
|
case 1:
|
||||||
|
return isfs_check_file(ctx, fst, path);
|
||||||
|
case 2:
|
||||||
|
return isfs_check_dir(ctx, fst, path);
|
||||||
|
default:
|
||||||
|
ISFS_debug("Unknown mode! (%d)\n", isfs_fst_get_type(fst));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_super_check_slot(isfs_ctx *ctx, u32 index)
|
||||||
|
{
|
||||||
|
u32 offs, cluster = CLUSTER_COUNT - (ctx->super_count - index) * ISFSSUPER_CLUSTERS;
|
||||||
|
u16* fat = isfs_get_fat(ctx);
|
||||||
|
|
||||||
|
for (offs = 0; offs < ISFSSUPER_CLUSTERS; offs++)
|
||||||
|
if (fat[cluster + offs] != FAT_CLUSTER_RESERVED)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_super_mark_bad_slot(isfs_ctx *ctx, u32 index)
|
||||||
|
{
|
||||||
|
u32 offs, cluster = CLUSTER_COUNT - (ctx->super_count - index) * ISFSSUPER_CLUSTERS;
|
||||||
|
u16* fat = isfs_get_fat(ctx);
|
||||||
|
|
||||||
|
for (offs = 0; offs < ISFSSUPER_CLUSTERS; offs++)
|
||||||
|
fat[cluster + offs] = FAT_CLUSTER_BAD;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_read_super(isfs_ctx *ctx, void *super, int index)
|
||||||
|
{
|
||||||
|
u32 cluster = CLUSTER_COUNT - (ctx->super_count - index) * ISFSSUPER_CLUSTERS;
|
||||||
|
isfs_hmac_meta seed = { .cluster = cluster };
|
||||||
|
return isfs_read_volume(ctx, cluster, ISFSSUPER_CLUSTERS, ISFSVOL_FLAG_HMAC, &seed, super);
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_write_super(isfs_ctx *ctx, void *super, int index)
|
||||||
|
{
|
||||||
|
u32 cluster = CLUSTER_COUNT - (ctx->super_count - index) * ISFSSUPER_CLUSTERS;
|
||||||
|
isfs_hmac_meta seed = { .cluster = cluster };
|
||||||
|
return isfs_write_volume(ctx, cluster, ISFSSUPER_CLUSTERS, ISFSVOL_FLAG_HMAC | ISFSVOL_FLAG_READBACK, &seed, super);
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_find_super(isfs_ctx* ctx, u32 min_generation, u32 max_generation, u32 *generation, u32 *version)
|
||||||
|
{
|
||||||
|
void* super = memalign(64, CLUSTER_SIZE);
|
||||||
|
if(!super) return -1;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int index;
|
||||||
|
u32 generation;
|
||||||
|
u8 version;
|
||||||
|
} newest = {-1, 0, 0};
|
||||||
|
|
||||||
|
for(int i = 0; i < ctx->super_count; i++)
|
||||||
|
{
|
||||||
|
u32 cluster = CLUSTER_COUNT - (ctx->super_count - i) * ISFSSUPER_CLUSTERS;
|
||||||
|
|
||||||
|
if(isfs_read_volume(ctx, cluster, 1, 0, NULL, super))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int cur_version = isfs_get_super_version(super);
|
||||||
|
if(cur_version < 0) continue;
|
||||||
|
|
||||||
|
u32 cur_generation = isfs_get_super_generation(super);
|
||||||
|
if((cur_generation < newest.generation) ||
|
||||||
|
(cur_generation < min_generation) ||
|
||||||
|
(cur_generation >= max_generation))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
newest.index = i;
|
||||||
|
newest.generation = cur_generation;
|
||||||
|
newest.version = cur_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(super);
|
||||||
|
|
||||||
|
if(newest.index == -1)
|
||||||
|
{
|
||||||
|
ISFS_debug("Failed to find super block.\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISFS_debug("Found super block (device=%s, version=%u, index=%d, generation=0x%lX)\n",
|
||||||
|
ctx->name, newest.version, newest.index, newest.generation);
|
||||||
|
|
||||||
|
if(generation) *generation = newest.generation;
|
||||||
|
if(version) *version = newest.version;
|
||||||
|
return newest.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_load_super(isfs_ctx* ctx, u32 min_generation, u32 max_generation)
|
||||||
|
{
|
||||||
|
ctx->generation = max_generation;
|
||||||
|
|
||||||
|
while((ctx->index = isfs_find_super(ctx, min_generation, ctx->generation, &ctx->generation, &ctx->version)) >= 0)
|
||||||
|
if(isfs_read_super(ctx, ctx->super, ctx->index) >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return (ctx->index >= 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_commit_super(isfs_ctx* ctx)
|
||||||
|
{
|
||||||
|
isfs_get_hdr(ctx)->generation++;
|
||||||
|
|
||||||
|
for(int i = 1; i <= ctx->super_count; i++)
|
||||||
|
{
|
||||||
|
u32 index = (ctx->index + i) & (ctx->super_count - 1);
|
||||||
|
|
||||||
|
if (isfs_super_check_slot(ctx, index) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (isfs_write_super(ctx, ctx->super, index) >= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
isfs_super_mark_bad_slot(ctx, index);
|
||||||
|
isfs_get_hdr(ctx)->generation++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
66
stage2/isfs/super.h
Normal file
66
stage2/isfs/super.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
#include "nand.h"
|
||||||
|
|
||||||
|
#define ISFSSUPER_CLUSTERS 0x10
|
||||||
|
#define ISFSSUPER_SIZE (ISFSSUPER_CLUSTERS * CLUSTER_SIZE)
|
||||||
|
|
||||||
|
typedef struct isfs_fst {
|
||||||
|
char name[12];
|
||||||
|
u8 mode;
|
||||||
|
u8 attr;
|
||||||
|
u16 sub;
|
||||||
|
u16 sib;
|
||||||
|
u32 size;
|
||||||
|
u16 x1;
|
||||||
|
u16 uid;
|
||||||
|
u16 gid;
|
||||||
|
u32 x3;
|
||||||
|
} PACKED isfs_fst;
|
||||||
|
_Static_assert(sizeof(isfs_fst) == 0x20, "isfs_fst size must be 0x20!");
|
||||||
|
|
||||||
|
typedef struct isfs_hdr {
|
||||||
|
char magic[4];
|
||||||
|
u32 generation;
|
||||||
|
u32 x1;
|
||||||
|
} PACKED isfs_hdr;
|
||||||
|
_Static_assert(sizeof(isfs_hdr) == 0xC, "isfs_hdr size must be 0xC!");
|
||||||
|
|
||||||
|
typedef struct isfs_super {
|
||||||
|
isfs_hdr hdr;
|
||||||
|
u16 fat[CLUSTER_COUNT];
|
||||||
|
isfs_fst fst[6143];
|
||||||
|
u8 pad[20];
|
||||||
|
} PACKED ALIGNED(64) isfs_super;
|
||||||
|
_Static_assert(sizeof(isfs_super) == ISFSSUPER_SIZE, "isfs_super must be 0x40000");
|
||||||
|
|
||||||
|
#define FAT_CLUSTER_LAST 0xFFFB // last cluster within a chain
|
||||||
|
#define FAT_CLUSTER_RESERVED 0xFFFC // reserved cluster
|
||||||
|
#define FAT_CLUSTER_BAD 0xFFFD // bad block (marked at factory)
|
||||||
|
#define FAT_CLUSTER_EMPTY 0xFFFE // empty (unused / available) space
|
||||||
|
|
||||||
|
typedef struct isfs_ctx isfs_ctx;
|
||||||
|
|
||||||
|
int isfs_get_super_version(void* buffer);
|
||||||
|
u32 isfs_get_super_generation(void* buffer);
|
||||||
|
|
||||||
|
isfs_hdr* isfs_get_hdr(isfs_ctx* ctx);
|
||||||
|
u16* isfs_get_fat(isfs_ctx* ctx);
|
||||||
|
isfs_fst* isfs_get_fst(isfs_ctx* ctx);
|
||||||
|
|
||||||
|
void isfs_print_fst(isfs_fst* fst);
|
||||||
|
|
||||||
|
int isfs_fst_get_type(const isfs_fst* fst);
|
||||||
|
bool isfs_fst_is_file(const isfs_fst* fst);
|
||||||
|
bool isfs_fst_is_dir(const isfs_fst* fst);
|
||||||
|
isfs_fst* isfs_find_fst(isfs_ctx* ctx, isfs_fst* fst, const char* path);
|
||||||
|
|
||||||
|
int isfs_super_check_slot(isfs_ctx *ctx, u32 index);
|
||||||
|
int isfs_super_mark_bad_slot(isfs_ctx *ctx, u32 index);
|
||||||
|
|
||||||
|
int isfs_read_super(isfs_ctx *ctx, void *super, int index);
|
||||||
|
int isfs_write_super(isfs_ctx *ctx, void *super, int index);
|
||||||
|
|
||||||
|
int isfs_find_super(isfs_ctx* ctx, u32 min_generation, u32 max_generation, u32 *generation, u32 *version);
|
||||||
|
int isfs_load_super(isfs_ctx* ctx, u32 min_generation, u32 max_generation);
|
||||||
|
int isfs_commit_super(isfs_ctx* ctx);
|
258
stage2/isfs/volume.c
Normal file
258
stage2/isfs/volume.c
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
#include "isfs/volume.h"
|
||||||
|
#include "nand.h"
|
||||||
|
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "aes.h"
|
||||||
|
#include "hmac.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
static u8 slc_super_buf[ISFSSUPER_SIZE];
|
||||||
|
/*static u8 slccmpt_super_buf[ISFSSUPER_SIZE];*/
|
||||||
|
|
||||||
|
isfs_ctx isfs[4] = {
|
||||||
|
[ISFSVOL_SLC]
|
||||||
|
{
|
||||||
|
.volume = ISFSVOL_SLC,
|
||||||
|
.name = "slc",
|
||||||
|
.bank = BANK_SLC,
|
||||||
|
.key = &otp.nand_key,
|
||||||
|
.hmac = &otp.nand_hmac,
|
||||||
|
.super_count = 64,
|
||||||
|
.super = slc_super_buf,
|
||||||
|
},
|
||||||
|
/*[ISFSVOL_SLCCMPT]
|
||||||
|
{
|
||||||
|
.volume = ISFSVOL_SLCCMPT,
|
||||||
|
.name = "slccmpt",
|
||||||
|
.bank = BANK_SLCCMPT,
|
||||||
|
.key = &otp.wii_nand_key,
|
||||||
|
.hmac = &otp.wii_nand_hmac,
|
||||||
|
.super_count = 16,
|
||||||
|
.super = slccmpt_super_buf,
|
||||||
|
},*/
|
||||||
|
};
|
||||||
|
|
||||||
|
int isfs_num_volumes(void)
|
||||||
|
{
|
||||||
|
return sizeof(isfs) / sizeof(isfs_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
isfs_ctx* isfs_get_volume(int volume)
|
||||||
|
{
|
||||||
|
if(volume < isfs_num_volumes() && volume >= 0)
|
||||||
|
return &isfs[volume];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* isfs_do_volume(const char* path, isfs_ctx** ctx)
|
||||||
|
{
|
||||||
|
isfs_ctx* volume = NULL;
|
||||||
|
|
||||||
|
if(!path) return NULL;
|
||||||
|
const char* filename = strchr(path, ':');
|
||||||
|
|
||||||
|
if(!filename) return NULL;
|
||||||
|
if(filename[1] != '/') return NULL;
|
||||||
|
|
||||||
|
char mount[sizeof(volume->name)] = {0};
|
||||||
|
memcpy(mount, path, filename - path);
|
||||||
|
|
||||||
|
for(int i = 0; i < isfs_num_volumes(); i++)
|
||||||
|
{
|
||||||
|
volume = &isfs[i];
|
||||||
|
if(strcmp(mount, volume->name)) continue;
|
||||||
|
|
||||||
|
if(!volume->mounted) return NULL;
|
||||||
|
|
||||||
|
*ctx = volume;
|
||||||
|
return (char*)(filename + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_read_volume(const isfs_ctx* ctx, u32 start_cluster, u32 cluster_count, u32 flags, void *hmac_seed, void *data)
|
||||||
|
{
|
||||||
|
u8 saved_hmacs[2][20] = {0}, hmac[20] = {0};
|
||||||
|
int rc = ISFSVOL_OK;
|
||||||
|
u32 i, p;
|
||||||
|
|
||||||
|
/* enable slc or slccmpt bank */
|
||||||
|
nand_enable_banks(ctx->bank);
|
||||||
|
|
||||||
|
/* read all requested clusters */
|
||||||
|
for (i = 0; i < cluster_count; i++)
|
||||||
|
{
|
||||||
|
u32 cluster = start_cluster + i;
|
||||||
|
u8 *cluster_data = (u8 *)data + i * CLUSTER_SIZE;
|
||||||
|
u32 cluster_start = cluster * CLUSTER_PAGES;
|
||||||
|
|
||||||
|
/* read cluster pages */
|
||||||
|
for (p = 0; p < CLUSTER_PAGES; p++)
|
||||||
|
{
|
||||||
|
u8 spare[SPARE_SIZE] = {0};
|
||||||
|
|
||||||
|
/* attempt to read the page (and correct ecc errors) */
|
||||||
|
rc = nand_read_page(cluster_start + p, &cluster_data[p * PAGE_SIZE], spare);
|
||||||
|
|
||||||
|
/* uncorrectable ecc error or other issues */
|
||||||
|
if (rc < 0)
|
||||||
|
return ISFSVOL_ERROR_READ;
|
||||||
|
|
||||||
|
/* ECC errors, a refresh might be needed */
|
||||||
|
if (rc > 0)
|
||||||
|
rc = ISFSVOL_ECC_CORRECTED;
|
||||||
|
|
||||||
|
/* page 6 and 7 store the hmac */
|
||||||
|
if (p == 6)
|
||||||
|
{
|
||||||
|
memcpy(saved_hmacs[0], &spare[1], 20);
|
||||||
|
memcpy(saved_hmacs[1], &spare[21], 12);
|
||||||
|
}
|
||||||
|
if (p == 7)
|
||||||
|
memcpy(&saved_hmacs[1][12], &spare[1], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decrypt cluster */
|
||||||
|
if (flags & ISFSVOL_FLAG_ENCRYPTED)
|
||||||
|
{
|
||||||
|
aes_reset();
|
||||||
|
aes_set_key(ctx->key);
|
||||||
|
aes_empty_iv();
|
||||||
|
aes_decrypt(cluster_data, cluster_data, CLUSTER_SIZE / AES_BLOCK_SIZE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify hmac */
|
||||||
|
if (flags & ISFSVOL_FLAG_HMAC)
|
||||||
|
{
|
||||||
|
hmac_ctx calc_hmac;
|
||||||
|
int matched = 0;
|
||||||
|
|
||||||
|
/* compute clusters hmac */
|
||||||
|
hmac_init(&calc_hmac, ctx->hmac, 20);
|
||||||
|
hmac_update(&calc_hmac, (const u8 *)hmac_seed, SHA_BLOCK_SIZE);
|
||||||
|
hmac_update(&calc_hmac, (const u8 *)data, cluster_count * CLUSTER_SIZE);
|
||||||
|
hmac_final(&calc_hmac, hmac);
|
||||||
|
|
||||||
|
/* ensure at least one of the saved hmacs matches */
|
||||||
|
matched += !memcmp(saved_hmacs[0], hmac, sizeof(hmac));
|
||||||
|
matched += !memcmp(saved_hmacs[1], hmac, sizeof(hmac));
|
||||||
|
|
||||||
|
if (matched == 2)
|
||||||
|
rc = ISFSVOL_OK;
|
||||||
|
else if (matched == 1)
|
||||||
|
rc = ISFSVOL_HMAC_PARTIAL;
|
||||||
|
else
|
||||||
|
rc = ISFSVOL_ERROR_HMAC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int isfs_write_volume(const isfs_ctx* ctx, u32 start_cluster, u32 cluster_count, u32 flags, void *hmac_seed, void *data)
|
||||||
|
{
|
||||||
|
static u8 blockpg[64][PAGE_SIZE] ALIGNED(64), blocksp[64][SPARE_SIZE];
|
||||||
|
static u8 pgbuf[PAGE_SIZE] ALIGNED(64), spbuf[SPARE_SIZE];
|
||||||
|
u8 hmac[20] = {0};
|
||||||
|
int rc = ISFSVOL_OK;
|
||||||
|
u32 b, p;
|
||||||
|
|
||||||
|
/* enable slc or slccmpt bank */
|
||||||
|
nand_enable_banks(ctx->bank);
|
||||||
|
|
||||||
|
/* compute clusters hmac */
|
||||||
|
if (flags & ISFSVOL_FLAG_HMAC)
|
||||||
|
{
|
||||||
|
hmac_ctx calc_hmac;
|
||||||
|
hmac_init(&calc_hmac, ctx->hmac, 20);
|
||||||
|
hmac_update(&calc_hmac, (const u8 *)hmac_seed, SHA_BLOCK_SIZE);
|
||||||
|
hmac_update(&calc_hmac, (const u8 *)data, cluster_count * CLUSTER_SIZE);
|
||||||
|
hmac_final(&calc_hmac, hmac);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup clusters encryption */
|
||||||
|
if (flags & ISFSVOL_FLAG_ENCRYPTED)
|
||||||
|
{
|
||||||
|
aes_reset();
|
||||||
|
aes_set_key(ctx->key);
|
||||||
|
aes_empty_iv();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 startpage = start_cluster * CLUSTER_PAGES;
|
||||||
|
u32 endpage = (start_cluster + cluster_count) * CLUSTER_PAGES;
|
||||||
|
|
||||||
|
u32 startblock = start_cluster / BLOCK_CLUSTERS;
|
||||||
|
u32 endblock = (start_cluster + cluster_count + BLOCK_CLUSTERS - 1) / BLOCK_CLUSTERS;
|
||||||
|
|
||||||
|
/* process data in nand blocks */
|
||||||
|
for (b = startblock; (b < endblock) && (rc >= 0); b++)
|
||||||
|
{
|
||||||
|
u32 firstblockpage = b * BLOCK_PAGES;
|
||||||
|
|
||||||
|
/* prepare block */
|
||||||
|
for (p = 0; p < 64; p++)
|
||||||
|
{
|
||||||
|
u32 curpage = firstblockpage + p; /* current page */
|
||||||
|
u32 clusidx = curpage % CLUSTER_PAGES; /* index in cluster */
|
||||||
|
|
||||||
|
/* if this page is unmodified, read it from nand */
|
||||||
|
if ((curpage < startpage) || (curpage >= endpage))
|
||||||
|
{
|
||||||
|
if (nand_read_page(curpage, blockpg[p], blocksp[p]) < 0)
|
||||||
|
return ISFSVOL_ERROR_READ;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* place hmac in page 6 and 7 of a cluster */
|
||||||
|
memset(blocksp[p], 0, SPARE_SIZE);
|
||||||
|
switch (clusidx)
|
||||||
|
{
|
||||||
|
case 6:
|
||||||
|
memcpy(&blocksp[p][1], hmac, 20);
|
||||||
|
memcpy(&blocksp[p][21], hmac, 12);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
memcpy(&blocksp[p][1], &hmac[12], 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encrypt or copy the data */
|
||||||
|
u8 *srcdata = (u8*)data + (curpage - startpage) * PAGE_SIZE;
|
||||||
|
if (flags & ISFSVOL_FLAG_ENCRYPTED)
|
||||||
|
aes_encrypt(blockpg[p], srcdata, PAGE_SIZE / AES_BLOCK_SIZE, clusidx > 0);
|
||||||
|
else
|
||||||
|
memcpy(blockpg[p], srcdata, PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* erase block */
|
||||||
|
if (nand_erase_block(b) < 0)
|
||||||
|
return ISFSVOL_ERROR_ERASE;
|
||||||
|
|
||||||
|
/* write block */
|
||||||
|
for (p = 0; p < BLOCK_PAGES; p++)
|
||||||
|
if (nand_write_page(firstblockpage + p, blockpg[p], blocksp[p]) < 0)
|
||||||
|
rc = ISFSVOL_ERROR_WRITE;
|
||||||
|
|
||||||
|
/* check if pages should be verified after writing */
|
||||||
|
if (rc || !(flags & ISFSVOL_FLAG_READBACK))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* read back pages */
|
||||||
|
for (p = 0; p < BLOCK_PAGES; p++)
|
||||||
|
{
|
||||||
|
if (nand_read_page(firstblockpage + p, pgbuf, spbuf) < 0)
|
||||||
|
return ISFSVOL_ERROR_READ;
|
||||||
|
|
||||||
|
/* page content doesn't match */
|
||||||
|
if (memcmp(blockpg[p], pgbuf, PAGE_SIZE) ||
|
||||||
|
memcmp(&blocksp[p][1], &spbuf[1], 0x20))
|
||||||
|
return ISFSVOL_ERROR_READBACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
25
stage2/isfs/volume.h
Normal file
25
stage2/isfs/volume.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
|
||||||
|
#define ISFSVOL_SLC 0
|
||||||
|
#define ISFSVOL_SLCCMPT 1
|
||||||
|
|
||||||
|
#define ISFSVOL_FLAG_HMAC 1
|
||||||
|
#define ISFSVOL_FLAG_ENCRYPTED 2
|
||||||
|
#define ISFSVOL_FLAG_READBACK 4
|
||||||
|
|
||||||
|
#define ISFSVOL_OK 0
|
||||||
|
#define ISFSVOL_ECC_CORRECTED 0x10
|
||||||
|
#define ISFSVOL_HMAC_PARTIAL 0x20
|
||||||
|
#define ISFSVOL_ERROR_WRITE -0x10
|
||||||
|
#define ISFSVOL_ERROR_READ -0x20
|
||||||
|
#define ISFSVOL_ERROR_ERASE -0x30
|
||||||
|
#define ISFSVOL_ERROR_HMAC -0x40
|
||||||
|
#define ISFSVOL_ERROR_READBACK -0x50
|
||||||
|
|
||||||
|
int isfs_num_volumes(void);
|
||||||
|
isfs_ctx* isfs_get_volume(int volume);
|
||||||
|
char* isfs_do_volume(const char* path, isfs_ctx** ctx);
|
||||||
|
|
||||||
|
int isfs_read_volume(const isfs_ctx* ctx, u32 start_cluster, u32 cluster_count, u32 flags, void *hmac_seed, void *data);
|
||||||
|
int isfs_write_volume(const isfs_ctx* ctx, u32 start_cluster, u32 cluster_count, u32 flags, void *hmac_seed, void *data);
|
338
stage2/latte.h
Normal file
338
stage2/latte.h
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LATTE_H
|
||||||
|
#define _LATTE_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Latte registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/Latte_Registers
|
||||||
|
*/
|
||||||
|
#define LT_REG_BASE (0x0D800000)
|
||||||
|
|
||||||
|
#define LT_IPC_PPCMSG_COMPAT (LT_REG_BASE + 0x000)
|
||||||
|
#define LT_IPC_PPCCTRL_COMPAT (LT_REG_BASE + 0x004)
|
||||||
|
#define LT_IPC_ARMMSG_COMPAT (LT_REG_BASE + 0x008)
|
||||||
|
#define LT_IPC_ARMCTRL_COMPAT (LT_REG_BASE + 0x00C)
|
||||||
|
|
||||||
|
#define LT_TIMER (LT_REG_BASE + 0x010)
|
||||||
|
#define LT_ALARM (LT_REG_BASE + 0x014)
|
||||||
|
|
||||||
|
#define LT_INTSR_PPC_COMPAT (LT_REG_BASE + 0x030)
|
||||||
|
#define LT_INTMR_PPC_COMPAT (LT_REG_BASE + 0x034)
|
||||||
|
#define LT_INTSR_ARM_COMPAT (LT_REG_BASE + 0x038)
|
||||||
|
#define LT_INTMR_ARM_COMPAT (LT_REG_BASE + 0x03C)
|
||||||
|
#define LT_INTMR_ARM2X_COMPAT (LT_REG_BASE + 0x040)
|
||||||
|
|
||||||
|
#define LT_UNK044 (LT_REG_BASE + 0x044)
|
||||||
|
#define LT_AHB_WDG_STATUS (LT_REG_BASE + 0x048)
|
||||||
|
#define LT_AHB_WDG_CONFIG (LT_REG_BASE + 0x04C)
|
||||||
|
#define LT_AHB_DMA_STATUS (LT_REG_BASE + 0x050)
|
||||||
|
#define LT_AHB_CPU_STATUS (LT_REG_BASE + 0x054)
|
||||||
|
#define LT_ERROR (LT_REG_BASE + 0x058)
|
||||||
|
#define LT_ERROR_MASK (LT_REG_BASE + 0x05C)
|
||||||
|
#define LT_MEMIRR (LT_REG_BASE + 0x060)
|
||||||
|
#define LT_AHBPROT (LT_REG_BASE + 0x064)
|
||||||
|
#define LT_UNK068 (LT_REG_BASE + 0x068)
|
||||||
|
#define LT_UNK06C (LT_REG_BASE + 0x06C)
|
||||||
|
#define LT_EXICTRL (LT_REG_BASE + 0x070)
|
||||||
|
#define LT_UNK074 (LT_REG_BASE + 0x074)
|
||||||
|
#define LT_UNK088 (LT_REG_BASE + 0x088)
|
||||||
|
|
||||||
|
#define LT_GPIOE_OUT (LT_REG_BASE + 0x0C0)
|
||||||
|
#define LT_GPIOE_DIR (LT_REG_BASE + 0x0C4)
|
||||||
|
#define LT_GPIOE_IN (LT_REG_BASE + 0x0C8)
|
||||||
|
#define LT_GPIOE_INTLVL (LT_REG_BASE + 0x0CC)
|
||||||
|
#define LT_GPIOE_INTFLAG (LT_REG_BASE + 0x0D0)
|
||||||
|
#define LT_GPIOE_INTMASK (LT_REG_BASE + 0x0D4)
|
||||||
|
#define LT_GPIOE_INMIR (LT_REG_BASE + 0x0D8)
|
||||||
|
|
||||||
|
#define LT_GPIO_ENABLE (LT_REG_BASE + 0x0DC)
|
||||||
|
#define LT_GPIO_OUT (LT_REG_BASE + 0x0E0)
|
||||||
|
#define LT_GPIO_DIR (LT_REG_BASE + 0x0E4)
|
||||||
|
#define LT_GPIO_IN (LT_REG_BASE + 0x0E8)
|
||||||
|
#define LT_GPIO_INTLVL (LT_REG_BASE + 0x0EC)
|
||||||
|
#define LT_GPIO_INTFLAG (LT_REG_BASE + 0x0F0)
|
||||||
|
#define LT_GPIO_INTMASK (LT_REG_BASE + 0x0F4)
|
||||||
|
#define LT_GPIO_INMIR (LT_REG_BASE + 0x0F8)
|
||||||
|
#define LT_GPIO_OWNER (LT_REG_BASE + 0x0FC)
|
||||||
|
|
||||||
|
#define LT_AHB_UNK100 (LT_REG_BASE + 0x100)
|
||||||
|
#define LT_AHB_UNK104 (LT_REG_BASE + 0x104)
|
||||||
|
#define LT_AHB_UNK108 (LT_REG_BASE + 0x108)
|
||||||
|
#define LT_AHB_UNK10C (LT_REG_BASE + 0x10C)
|
||||||
|
#define LT_AHB_UNK110 (LT_REG_BASE + 0x110)
|
||||||
|
#define LT_AHB_UNK114 (LT_REG_BASE + 0x114)
|
||||||
|
#define LT_AHB_UNK118 (LT_REG_BASE + 0x118)
|
||||||
|
#define LT_AHB_UNK11C (LT_REG_BASE + 0x11C)
|
||||||
|
#define LT_AHB_UNK120 (LT_REG_BASE + 0x120)
|
||||||
|
#define LT_AHB_UNK124 (LT_REG_BASE + 0x124)
|
||||||
|
#define LT_AHB_UNK130 (LT_REG_BASE + 0x130)
|
||||||
|
#define LT_AHB_UNK134 (LT_REG_BASE + 0x134)
|
||||||
|
#define LT_AHB_UNK138 (LT_REG_BASE + 0x138)
|
||||||
|
|
||||||
|
#define LT_ARB_CFG (LT_REG_BASE + 0x140)
|
||||||
|
#define LT_DIFLAGS (LT_REG_BASE + 0x180)
|
||||||
|
#define LT_RESETS_AHB (LT_REG_BASE + 0x184)
|
||||||
|
#define LT_COMPAT_MEMCTRL_WORKAROUND (LT_REG_BASE + 0x188)
|
||||||
|
#define LT_BOOT0 (LT_REG_BASE + 0x18C)
|
||||||
|
#define LT_CLOCKINFO (LT_REG_BASE + 0x190)
|
||||||
|
#define LT_RESETS_COMPAT (LT_REG_BASE + 0x194)
|
||||||
|
#define LT_CLOCKGATE_COMPAT (LT_REG_BASE + 0x198)
|
||||||
|
#define LT_SATA_UNK1A8 (LT_REG_BASE + 0x1A8)
|
||||||
|
#define LT_SATA_UNK1C8 (LT_REG_BASE + 0x1C8)
|
||||||
|
#define LT_SATA_UNK1CC (LT_REG_BASE + 0x1CC)
|
||||||
|
#define LT_SATA_UNK1D0 (LT_REG_BASE + 0x1D0)
|
||||||
|
#define LT_UNK1D8 (LT_REG_BASE + 0x1D8)
|
||||||
|
#define LT_IOPOWER (LT_REG_BASE + 0x1DC)
|
||||||
|
#define LT_IOSTRENGTH_CTRL0 (LT_REG_BASE + 0x1E0)
|
||||||
|
#define LT_IOSTRENGTH_CTRL1 (LT_REG_BASE + 0x1E4)
|
||||||
|
#define LT_ACRCLK_STRENGTH_CTRL (LT_REG_BASE + 0x1E8)
|
||||||
|
#define LT_OTPCMD (LT_REG_BASE + 0x1EC)
|
||||||
|
#define LT_OTPDATA (LT_REG_BASE + 0x1F0)
|
||||||
|
#define LT_UNK204 (LT_REG_BASE + 0x204)
|
||||||
|
#define LT_ASICREV_ACR (LT_REG_BASE + 0x214)
|
||||||
|
#define LT_UNK224 (LT_REG_BASE + 0x224)
|
||||||
|
#define LT_UNK250 (LT_REG_BASE + 0x250)
|
||||||
|
#define LT_UNK254 (LT_REG_BASE + 0x254)
|
||||||
|
#define LT_UNK258 (LT_REG_BASE + 0x258)
|
||||||
|
|
||||||
|
#define LT_IPC_PPC0_PPCMSG (LT_REG_BASE + 0x400)
|
||||||
|
#define LT_IPC_PPC0_PPCCTRL (LT_REG_BASE + 0x404)
|
||||||
|
#define LT_IPC_PPC0_ARMMSG (LT_REG_BASE + 0x408)
|
||||||
|
#define LT_IPC_PPC0_ARMCTRL (LT_REG_BASE + 0x40C)
|
||||||
|
#define LT_IPC_PPC1_PPCMSG (LT_REG_BASE + 0x410)
|
||||||
|
#define LT_IPC_PPC1_PPCCTRL (LT_REG_BASE + 0x414)
|
||||||
|
#define LT_IPC_PPC1_ARMMSG (LT_REG_BASE + 0x418)
|
||||||
|
#define LT_IPC_PPC1_ARMCTRL (LT_REG_BASE + 0x41C)
|
||||||
|
#define LT_IPC_PPC2_PPCMSG (LT_REG_BASE + 0x420)
|
||||||
|
#define LT_IPC_PPC2_PPCCTRL (LT_REG_BASE + 0x424)
|
||||||
|
#define LT_IPC_PPC2_ARMMSG (LT_REG_BASE + 0x428)
|
||||||
|
#define LT_IPC_PPC2_ARMCTRL (LT_REG_BASE + 0x42C)
|
||||||
|
|
||||||
|
#define LT_INTSR_AHBALL_PPC0 (LT_REG_BASE + 0x440)
|
||||||
|
#define LT_INTSR_AHBLT_PPC0 (LT_REG_BASE + 0x444)
|
||||||
|
#define LT_INTMR_AHBALL_PPC0 (LT_REG_BASE + 0x448)
|
||||||
|
#define LT_INTMR_AHBLT_PPC0 (LT_REG_BASE + 0x44C)
|
||||||
|
#define LT_INTSR_AHBALL_PPC1 (LT_REG_BASE + 0x450)
|
||||||
|
#define LT_INTSR_AHBLT_PPC1 (LT_REG_BASE + 0x454)
|
||||||
|
#define LT_INTMR_AHBALL_PPC1 (LT_REG_BASE + 0x458)
|
||||||
|
#define LT_INTMR_AHBLT_PPC1 (LT_REG_BASE + 0x45C)
|
||||||
|
#define LT_INTSR_AHBALL_PPC2 (LT_REG_BASE + 0x460)
|
||||||
|
#define LT_INTSR_AHBLT_PPC2 (LT_REG_BASE + 0x464)
|
||||||
|
#define LT_INTMR_AHBALL_PPC2 (LT_REG_BASE + 0x468)
|
||||||
|
#define LT_INTMR_AHBLT_PPC2 (LT_REG_BASE + 0x46C)
|
||||||
|
#define LT_INTSR_AHBALL_ARM (LT_REG_BASE + 0x470)
|
||||||
|
#define LT_INTSR_AHBLT_ARM (LT_REG_BASE + 0x474)
|
||||||
|
#define LT_INTMR_AHBALL_ARM (LT_REG_BASE + 0x478)
|
||||||
|
#define LT_INTMR_AHBLT_ARM (LT_REG_BASE + 0x47C)
|
||||||
|
#define LT_INTMR_AHBALL_ARM2X (LT_REG_BASE + 0x480)
|
||||||
|
#define LT_INTMR_AHBLT_ARM2X (LT_REG_BASE + 0x484)
|
||||||
|
|
||||||
|
#define LT_AHB2_WDG_STATUS (LT_REG_BASE + 0x4A0)
|
||||||
|
#define LT_AHB2_DMA_STATUS (LT_REG_BASE + 0x4A4)
|
||||||
|
#define LT_AHB2_CPU_STATUS (LT_REG_BASE + 0x4A8)
|
||||||
|
|
||||||
|
#define LT_UNK4C8 (LT_REG_BASE + 0x4C8)
|
||||||
|
#define LT_UNK4CC (LT_REG_BASE + 0x4CC)
|
||||||
|
#define LT_UNK4D0 (LT_REG_BASE + 0x4D0)
|
||||||
|
#define LT_UNK4D4 (LT_REG_BASE + 0x4D4)
|
||||||
|
#define LT_UNK4DC (LT_REG_BASE + 0x4DC)
|
||||||
|
#define LT_UNK4E0 (LT_REG_BASE + 0x4E0)
|
||||||
|
#define LT_UNK4E4 (LT_REG_BASE + 0x4E4)
|
||||||
|
#define LT_UNK500 (LT_REG_BASE + 0x500)
|
||||||
|
#define LT_UNK504 (LT_REG_BASE + 0x504)
|
||||||
|
|
||||||
|
#define LT_OTPPROT (LT_REG_BASE + 0x510)
|
||||||
|
#define LT_SYSPROT (LT_REG_BASE + 0x514)
|
||||||
|
|
||||||
|
#define LT_GPIOE2_OUT (LT_REG_BASE + 0x520)
|
||||||
|
#define LT_GPIOE2_DIR (LT_REG_BASE + 0x524)
|
||||||
|
#define LT_GPIOE2_IN (LT_REG_BASE + 0x528)
|
||||||
|
#define LT_GPIOE2_INTLVL (LT_REG_BASE + 0x52C)
|
||||||
|
#define LT_GPIOE2_INTFLAG (LT_REG_BASE + 0x530)
|
||||||
|
#define LT_GPIOE2_INTMASK (LT_REG_BASE + 0x534)
|
||||||
|
#define LT_GPIOE2_INMIR (LT_REG_BASE + 0x538)
|
||||||
|
|
||||||
|
#define LT_GPIO2_ENABLE (LT_REG_BASE + 0x53C)
|
||||||
|
#define LT_GPIO2_OUT (LT_REG_BASE + 0x540)
|
||||||
|
#define LT_GPIO2_DIR (LT_REG_BASE + 0x544)
|
||||||
|
#define LT_GPIO2_IN (LT_REG_BASE + 0x548)
|
||||||
|
#define LT_GPIO2_INTLVL (LT_REG_BASE + 0x54C)
|
||||||
|
#define LT_GPIO2_INTFLAG (LT_REG_BASE + 0x550)
|
||||||
|
#define LT_GPIO2_INTMASK (LT_REG_BASE + 0x554)
|
||||||
|
#define LT_GPIO2_INMIR (LT_REG_BASE + 0x558)
|
||||||
|
#define LT_GPIO2_OWNER (LT_REG_BASE + 0x55C)
|
||||||
|
|
||||||
|
#define LT_I2C_CLOCK (LT_REG_BASE + 0x570)
|
||||||
|
#define LT_I2C_INOUT_DATA (LT_REG_BASE + 0x574)
|
||||||
|
#define LT_I2C_INOUT_CTRL (LT_REG_BASE + 0x578)
|
||||||
|
#define LT_I2C_INOUT_SIZE (LT_REG_BASE + 0x57C)
|
||||||
|
#define LT_I2C_INT_MASK (LT_REG_BASE + 0x580)
|
||||||
|
#define LT_I2C_INT_STATE (LT_REG_BASE + 0x584)
|
||||||
|
|
||||||
|
#define LT_ASICREV_CCR (LT_REG_BASE + 0x5A0)
|
||||||
|
#define LT_DEBUG (LT_REG_BASE + 0x5A4)
|
||||||
|
#define LT_COMPAT_MEMCTRL_STATE (LT_REG_BASE + 0x5B0)
|
||||||
|
#define LT_COMPAT_AHB_STATE (LT_REG_BASE + 0x5B4)
|
||||||
|
#define LT_COMPAT_STEREO_OUT_SELECT (LT_REG_BASE + 0x5B8)
|
||||||
|
#define LT_IOP2X (LT_REG_BASE + 0x5BC)
|
||||||
|
#define LT_UNK5C0 (LT_REG_BASE + 0x5C0)
|
||||||
|
#define LT_IOSTRENGTH_CTRL2 (LT_REG_BASE + 0x5C8)
|
||||||
|
#define LT_UNK5CC (LT_REG_BASE + 0x5CC)
|
||||||
|
#define LT_RESETS (LT_REG_BASE + 0x5E0)
|
||||||
|
#define LT_RESETS_AHMN (LT_REG_BASE + 0x5E4)
|
||||||
|
#define LT_CLOCKGATE (LT_REG_BASE + 0x5E8)
|
||||||
|
#define LT_SYSPLL_CFG (LT_REG_BASE + 0x5EC)
|
||||||
|
#define LT_ABIF_CPLTL_OFFSET (LT_REG_BASE + 0x620)
|
||||||
|
#define LT_ABIF_CPLTL_DATA (LT_REG_BASE + 0x624)
|
||||||
|
#define LT_UNK628 (LT_REG_BASE + 0x628)
|
||||||
|
#define LT_60XE_CFG (LT_REG_BASE + 0x640)
|
||||||
|
#define LT_UNK660 (LT_REG_BASE + 0x660)
|
||||||
|
#define LT_UNK640 (LT_REG_BASE + 0x640)
|
||||||
|
#define LT_DCCMPT (LT_REG_BASE + 0x708)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NAND registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/NAND_Interface
|
||||||
|
*/
|
||||||
|
#define NAND_REG_BASE (0x0D010000)
|
||||||
|
|
||||||
|
#define NAND_CTRL (NAND_REG_BASE + 0x000)
|
||||||
|
#define NAND_CONF (NAND_REG_BASE + 0x004)
|
||||||
|
#define NAND_ADDR0 (NAND_REG_BASE + 0x008)
|
||||||
|
#define NAND_ADDR1 (NAND_REG_BASE + 0x00C)
|
||||||
|
#define NAND_DATA (NAND_REG_BASE + 0x010)
|
||||||
|
#define NAND_ECC (NAND_REG_BASE + 0x014)
|
||||||
|
#define NAND_BANK (NAND_REG_BASE + 0x018)
|
||||||
|
#define NAND_UNK1 (NAND_REG_BASE + 0x01C)
|
||||||
|
#define NAND_BANK_CTRL (NAND_REG_BASE + 0x030)
|
||||||
|
#define NAND_UNK3 (NAND_REG_BASE + 0x040)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AES registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/AES_Engine
|
||||||
|
*/
|
||||||
|
#define AES_REG_BASE (0x0D020000)
|
||||||
|
|
||||||
|
#define AES_CTRL (AES_REG_BASE + 0x000)
|
||||||
|
#define AES_SRC (AES_REG_BASE + 0x004)
|
||||||
|
#define AES_DEST (AES_REG_BASE + 0x008)
|
||||||
|
#define AES_KEY (AES_REG_BASE + 0x00C)
|
||||||
|
#define AES_IV (AES_REG_BASE + 0x010)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SHA-1 registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/SHA-1_Engine
|
||||||
|
*/
|
||||||
|
#define SHA_REG_BASE (0x0D030000)
|
||||||
|
|
||||||
|
#define SHA_CTRL (SHA_REG_BASE + 0x000)
|
||||||
|
#define SHA_SRC (SHA_REG_BASE + 0x004)
|
||||||
|
#define SHA_H0 (SHA_REG_BASE + 0x008)
|
||||||
|
#define SHA_H1 (SHA_REG_BASE + 0x00C)
|
||||||
|
#define SHA_H2 (SHA_REG_BASE + 0x010)
|
||||||
|
#define SHA_H3 (SHA_REG_BASE + 0x014)
|
||||||
|
#define SHA_H4 (SHA_REG_BASE + 0x018)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SD Host Controller registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/SD_Host_Controller
|
||||||
|
*/
|
||||||
|
#define SD0_REG_BASE (0x0D070000)
|
||||||
|
#define SD1_REG_BASE (0x0D080000)
|
||||||
|
#define SD2_REG_BASE (0x0D100000)
|
||||||
|
#define SD3_REG_BASE (0x0D110000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OHCI registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/USB_Host_Controller
|
||||||
|
*/
|
||||||
|
#define OHCI0_REG_BASE (0x0D050000)
|
||||||
|
#define OHCI1_REG_BASE (0x0D060000)
|
||||||
|
#define OHCI10_REG_BASE (0x0D130000)
|
||||||
|
#define OHCI20_REG_BASE (0x0D150000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EHCI registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/USB_Host_Controller
|
||||||
|
*/
|
||||||
|
#define EHCI0_REG_BASE (0x0D040000)
|
||||||
|
#define EHCI1_REG_BASE (0x0D120000)
|
||||||
|
#define EHCI2_REG_BASE (0x0D140000)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EXI registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/Legacy#External_Interface
|
||||||
|
*/
|
||||||
|
#define EXI_REG_BASE (0x0D806800)
|
||||||
|
|
||||||
|
#define EXI0_REG_BASE (EXI_REG_BASE + 0x000)
|
||||||
|
#define EXI1_REG_BASE (EXI_REG_BASE + 0x014)
|
||||||
|
#define EXI2_REG_BASE (EXI_REG_BASE + 0x028)
|
||||||
|
#define EXIBOOT_REG_BASE (EXI_REG_BASE + 0x040)
|
||||||
|
|
||||||
|
#define EXI0_CSR (EXI0_REG_BASE + 0x000)
|
||||||
|
#define EXI0_MAR (EXI0_REG_BASE + 0x004)
|
||||||
|
#define EXI0_LENGTH (EXI0_REG_BASE + 0x008)
|
||||||
|
#define EXI0_CR (EXI0_REG_BASE + 0x00C)
|
||||||
|
#define EXI0_DATA (EXI0_REG_BASE + 0x010)
|
||||||
|
|
||||||
|
#define EXI1_CSR (EXI1_REG_BASE + 0x000)
|
||||||
|
#define EXI1_MAR (EXI1_REG_BASE + 0x004)
|
||||||
|
#define EXI1_LENGTH (EXI1_REG_BASE + 0x008)
|
||||||
|
#define EXI1_CR (EXI1_REG_BASE + 0x00C)
|
||||||
|
#define EXI1_DATA (EXI1_REG_BASE + 0x010)
|
||||||
|
|
||||||
|
#define EXI2_CSR (EXI2_REG_BASE + 0x000)
|
||||||
|
#define EXI2_MAR (EXI2_REG_BASE + 0x004)
|
||||||
|
#define EXI2_LENGTH (EXI2_REG_BASE + 0x008)
|
||||||
|
#define EXI2_CR (EXI2_REG_BASE + 0x00C)
|
||||||
|
#define EXI2_DATA (EXI2_REG_BASE + 0x010)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory Controller registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/Memory_Controller
|
||||||
|
*/
|
||||||
|
#define MEM_REG_BASE (0x0D8B4000)
|
||||||
|
|
||||||
|
#define MEM_PROT (MEM_REG_BASE + 0x20A)
|
||||||
|
#define MEM_PROT_START (MEM_REG_BASE + 0x20C)
|
||||||
|
#define MEM_PROT_END (MEM_REG_BASE + 0x20E)
|
||||||
|
#define MEM_REFRESH_FLAG (MEM_REG_BASE + 0x226)
|
||||||
|
#define MEM_FLUSH_MASK (MEM_REG_BASE + 0x228)
|
||||||
|
#define MEM_FLUSH_ACK (MEM_REG_BASE + 0x22A)
|
||||||
|
#define MEM_SEQ_REG_VAL (MEM_REG_BASE + 0x2C4)
|
||||||
|
#define MEM_SEQ_REG_ADDR (MEM_REG_BASE + 0x2C6)
|
||||||
|
#define MEM_SEQ0_REG_VAL (MEM_REG_BASE + 0x300)
|
||||||
|
#define MEM_SEQ0_REG_ADDR (MEM_REG_BASE + 0x302)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AHMN registers.
|
||||||
|
* http://wiiubrew.org/wiki/Hardware/XN_Controller
|
||||||
|
*/
|
||||||
|
#define AHMN_REG_BASE (0x0D8B0800)
|
||||||
|
|
||||||
|
#define AHMN_MEM0_CONFIG (AHMN_REG_BASE + 0x000)
|
||||||
|
#define AHMN_MEM1_CONFIG (AHMN_REG_BASE + 0x004)
|
||||||
|
#define AHMN_MEM2_CONFIG (AHMN_REG_BASE + 0x008)
|
||||||
|
#define AHMN_RDBI_MASK (AHMN_REG_BASE + 0x00C)
|
||||||
|
#define AHMN_ERROR_MASK (AHMN_REG_BASE + 0x020)
|
||||||
|
#define AHMN_ERROR (AHMN_REG_BASE + 0x024)
|
||||||
|
#define AHMN_UNK40 (AHMN_REG_BASE + 0x040)
|
||||||
|
#define AHMN_UNK44 (AHMN_REG_BASE + 0x044)
|
||||||
|
#define AHMN_TRANSFER_STATE (AHMN_REG_BASE + 0x050)
|
||||||
|
#define AHMN_WORKAROUND (AHMN_REG_BASE + 0x054)
|
||||||
|
|
||||||
|
#define AHMN_MEM0 (AHMN_REG_BASE + 0x100)
|
||||||
|
#define AHMN_MEM1 (AHMN_REG_BASE + 0x200)
|
||||||
|
#define AHMN_MEM2 (AHMN_REG_BASE + 0x400)
|
||||||
|
|
||||||
|
#endif
|
106
stage2/link.ld
Normal file
106
stage2/link.ld
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
linker script
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
|
||||||
|
# This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
OUTPUT_FORMAT("elf32-bigarm", "elf32-bigarm", "elf32-bigarm")
|
||||||
|
OUTPUT_ARCH(arm)
|
||||||
|
EXTERN(_start)
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
__stack_size = 0x80000;
|
||||||
|
__irqstack_size = 0x40000;
|
||||||
|
__excstack_size = 0x40000;
|
||||||
|
|
||||||
|
MEMORY {
|
||||||
|
sram0 : ORIGIN = 0xffff0000, LENGTH = 64K
|
||||||
|
mem2 : ORIGIN = 0x10100000, LENGTH = 60M
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.init :
|
||||||
|
{
|
||||||
|
*(.init)
|
||||||
|
*(.sram.text)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >sram0
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
*(.text*)
|
||||||
|
*(.text.*)
|
||||||
|
*(.gnu.warning)
|
||||||
|
*(.gnu.linkonce.t*)
|
||||||
|
*(.glue_7)
|
||||||
|
*(.glue_7t)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >mem2
|
||||||
|
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
*(.rodata)
|
||||||
|
*all.rodata*(*)
|
||||||
|
*(.roda)
|
||||||
|
*(.rodata.*)
|
||||||
|
*(.gnu.linkonce.r*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >mem2
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
*(.data)
|
||||||
|
*(.data.*)
|
||||||
|
*(.gnu.linkonce.d*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >mem2
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
__bss_start = . ;
|
||||||
|
*(.dynbss)
|
||||||
|
*(.gnu.linkonce.b*)
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_end = . ;
|
||||||
|
} >mem2
|
||||||
|
|
||||||
|
.stack :
|
||||||
|
{
|
||||||
|
. = ALIGN(16);
|
||||||
|
__stack_end = .;
|
||||||
|
. = . +__stack_size;
|
||||||
|
. = ALIGN(16);
|
||||||
|
__stack_addr = .;
|
||||||
|
__irqstack_end = .;
|
||||||
|
. = . +__irqstack_size;
|
||||||
|
. = ALIGN(16);
|
||||||
|
__irqstack_addr = .;
|
||||||
|
__excstack_end = .;
|
||||||
|
. = . +__excstack_size;
|
||||||
|
. = ALIGN(16);
|
||||||
|
__excstack_addr = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >mem2
|
||||||
|
|
||||||
|
.heap :
|
||||||
|
{
|
||||||
|
__end__ = .;
|
||||||
|
__heap_start__ = .;
|
||||||
|
__heap_end__ = (ORIGIN(mem2) + LENGTH(mem2));
|
||||||
|
. = __heap_end__;
|
||||||
|
} >mem2
|
||||||
|
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
*(.ARM.extab*)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
stage2/link.specs
Normal file
4
stage2/link.specs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
%rename link old_link
|
||||||
|
|
||||||
|
*link:
|
||||||
|
%(old_link) -T ../stage2/link.ld%s
|
98
stage2/lolserial_asm.S
Normal file
98
stage2/lolserial_asm.S
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Roberto Van Eeden <rwrr0644@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LOLSERIAL_DEBUG
|
||||||
|
|
||||||
|
#include "latte.h"
|
||||||
|
|
||||||
|
.arm
|
||||||
|
|
||||||
|
.globl lolserial_print
|
||||||
|
.globl lolserial_lprint
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the number of timer ticks to wait
|
||||||
|
* each bit; might need manual adjustment
|
||||||
|
* for particoular serial adapters, usually
|
||||||
|
* the stable value is in the +5/-5 range
|
||||||
|
*/
|
||||||
|
|
||||||
|
.equ LOLSERIAL_WAIT_TICKS, 200
|
||||||
|
.equ GP_SENSORBAR, 0x00000100
|
||||||
|
.equ GP_SENSORBAR_SHIFT, 8
|
||||||
|
|
||||||
|
lolserial_print:
|
||||||
|
mov r1, #-1
|
||||||
|
lolserial_lprint:
|
||||||
|
push {r5-r6}
|
||||||
|
add r1, r1, r0
|
||||||
|
|
||||||
|
ldr r6, =0x0D800000
|
||||||
|
|
||||||
|
/* clear32(LT_GPIO_OWNER, GP_SENSORBAR) */
|
||||||
|
ldr r5, [r6, #0x0FC]
|
||||||
|
bic r5, r5, #GP_SENSORBAR
|
||||||
|
str r5, [r6, #0x0FC]
|
||||||
|
|
||||||
|
/* set32(LT_GPIO_ENABLE, GP_SENSORBAR) */
|
||||||
|
ldr r5, [r6, #0x0DC]
|
||||||
|
orr r5, r5, #GP_SENSORBAR
|
||||||
|
str r5, [r6, #0x0DC]
|
||||||
|
|
||||||
|
/* set32(LT_GPIO_DIR, GP_SENSORBAR) */
|
||||||
|
ldr r5, [r6, #0x0E4]
|
||||||
|
orr r5, r5, #GP_SENSORBAR
|
||||||
|
str r5, [r6, #0x0E4]
|
||||||
|
|
||||||
|
/* set32(LT_GPIO_OUT, GP_SENSORBAR) */
|
||||||
|
ldr r5, [r6, #0x0E0]
|
||||||
|
orr r5, r5, #GP_SENSORBAR
|
||||||
|
str r5, [r6, #0x0E0]
|
||||||
|
|
||||||
|
lolserial_send_string_loop:
|
||||||
|
cmp r0, r1
|
||||||
|
ldrneb r5, [r0], #1
|
||||||
|
cmpne r5, #0
|
||||||
|
beq lolserial_send_string_end
|
||||||
|
|
||||||
|
mov r3, #0x200
|
||||||
|
orr r3, r3, r5, lsl #1
|
||||||
|
|
||||||
|
lolserial_send_char_loop:
|
||||||
|
and r4, r3, #1
|
||||||
|
|
||||||
|
ldr r5, [r6, #0x0E0]
|
||||||
|
bic r5, r5, #GP_SENSORBAR
|
||||||
|
orr r5, r5, r4, lsl #GP_SENSORBAR_SHIFT
|
||||||
|
str r5, [r6, #0x0E0]
|
||||||
|
|
||||||
|
ldr r5, [r6, #0x010]
|
||||||
|
adds r4, r5, #LOLSERIAL_WAIT_TICKS
|
||||||
|
bcc timer_wait_loop
|
||||||
|
timer_wait_overflow_loop:
|
||||||
|
ldr r2, [r6, #0x010]
|
||||||
|
cmp r2, r5
|
||||||
|
bhs timer_wait_overflow_loop
|
||||||
|
timer_wait_loop:
|
||||||
|
ldr r5, [r6, #0x010]
|
||||||
|
cmp r5, r4
|
||||||
|
blo timer_wait_loop
|
||||||
|
|
||||||
|
movs r3, r3, lsr #1
|
||||||
|
bne lolserial_send_char_loop
|
||||||
|
|
||||||
|
b lolserial_send_string_loop
|
||||||
|
lolserial_send_string_end:
|
||||||
|
|
||||||
|
pop {r5-r6}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
#endif /* LOLSERIAL_DEBUG */
|
129
stage2/main.c
Normal file
129
stage2/main.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* ISFSHAX
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 rw-r-r-0644 <rwrr0644@gmail.com>
|
||||||
|
*
|
||||||
|
* Based on code from Minute and Mini:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Ash Logan <quarktheawesome@gmail.com>
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
* Copyright (C) 2009 Andre Heider "dhewg" <dhewg@wiibrew.org>
|
||||||
|
* Copyright (C) 2009 John Kelley <wiidev@kelley.ca>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "memory.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "smc.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "isfs/isfshax.h"
|
||||||
|
#include "sdcard.h"
|
||||||
|
#include "fatfs/ff.h"
|
||||||
|
#include "nand.h"
|
||||||
|
#include "isfs/isfs.h"
|
||||||
|
#include "ancast.h"
|
||||||
|
|
||||||
|
u32 load_payload_sd(void)
|
||||||
|
{
|
||||||
|
static FATFS fatfs;
|
||||||
|
UINT btr, br;
|
||||||
|
FRESULT res;
|
||||||
|
FIL file;
|
||||||
|
u32 vector = 0;
|
||||||
|
|
||||||
|
sdcard_init();
|
||||||
|
|
||||||
|
res = f_mount(&fatfs, "0:", 1);
|
||||||
|
if (res)
|
||||||
|
goto error_mount;
|
||||||
|
|
||||||
|
res = f_open(&file, "isfshax.bin", FA_READ);
|
||||||
|
if (res)
|
||||||
|
goto error_open;
|
||||||
|
|
||||||
|
btr = f_size(&file);
|
||||||
|
if (!btr)
|
||||||
|
goto error_read;
|
||||||
|
|
||||||
|
res = f_read(&file, (u8*)ANCAST_ADDRESS_IOP, btr, &br);
|
||||||
|
if (res || (btr != br))
|
||||||
|
goto error_read;
|
||||||
|
|
||||||
|
vector = ancast_iop_load((u8*)ANCAST_ADDRESS_IOP, btr);
|
||||||
|
|
||||||
|
error_read:
|
||||||
|
f_close(&file);
|
||||||
|
error_open:
|
||||||
|
f_mount(0, "0:", 0);
|
||||||
|
error_mount:
|
||||||
|
sdcard_exit();
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 load_payload_nand(void)
|
||||||
|
{
|
||||||
|
isfs_file file;
|
||||||
|
size_t btr, br;
|
||||||
|
int res;
|
||||||
|
u32 vector = 0;
|
||||||
|
|
||||||
|
res = isfs_init();
|
||||||
|
if (res)
|
||||||
|
return vector;
|
||||||
|
|
||||||
|
res = isfs_open(&file, "slc:/isfshax.bin");
|
||||||
|
if (res)
|
||||||
|
goto error_open;
|
||||||
|
|
||||||
|
btr = file.fst->size;
|
||||||
|
if (!btr)
|
||||||
|
goto error_read;
|
||||||
|
|
||||||
|
res = isfs_read(&file, (u8*)ANCAST_ADDRESS_IOP, btr, &br);
|
||||||
|
if (res || (btr != br))
|
||||||
|
goto error_read;
|
||||||
|
|
||||||
|
vector = ancast_iop_load((u8*)ANCAST_ADDRESS_IOP, btr);
|
||||||
|
|
||||||
|
error_read:
|
||||||
|
isfs_close(&file);
|
||||||
|
error_open:
|
||||||
|
isfs_fini();
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 _main(void)
|
||||||
|
{
|
||||||
|
u32 vector = 0;
|
||||||
|
mem_initialize();
|
||||||
|
irq_initialize();
|
||||||
|
crypto_read_otp();
|
||||||
|
nand_initialize();
|
||||||
|
|
||||||
|
/* repair isfshax superblocks ecc errors, if present */
|
||||||
|
isfshax_refresh();
|
||||||
|
|
||||||
|
/* attempt to load the payload from SD, then NAND */
|
||||||
|
vector = load_payload_sd();
|
||||||
|
if (!vector)
|
||||||
|
vector = load_payload_nand();
|
||||||
|
|
||||||
|
nand_deinitialize();
|
||||||
|
irq_shutdown();
|
||||||
|
mem_shutdown();
|
||||||
|
|
||||||
|
/* failed to load the payload from SD or NAND -> shutdown */
|
||||||
|
if (!vector)
|
||||||
|
smc_shutdown(false);
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
400
stage2/memory.c
Normal file
400
stage2/memory.c
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
void _dc_inval_entries(void *start, int count);
|
||||||
|
void _dc_flush_entries(const void *start, int count);
|
||||||
|
void _dc_flush(void);
|
||||||
|
void _ic_inval(void);
|
||||||
|
void _drain_write_buffer(void);
|
||||||
|
|
||||||
|
#ifndef LOADER
|
||||||
|
static u32 *__page_table;
|
||||||
|
void _dc_inval(void);
|
||||||
|
void _tlb_inval(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LINESIZE 0x20
|
||||||
|
#define CACHESIZE 0x4000
|
||||||
|
|
||||||
|
#define CR_MMU (1 << 0)
|
||||||
|
#define CR_DCACHE (1 << 2)
|
||||||
|
#define CR_ICACHE (1 << 12)
|
||||||
|
|
||||||
|
// this is ripped from IOS, because no one can figure out just WTF this thing is doing
|
||||||
|
void _ahb_flush_to(enum rb_client dev) {
|
||||||
|
u32 mask;
|
||||||
|
switch(dev) {
|
||||||
|
case RB_IOD: mask = 0x8000; break;
|
||||||
|
case RB_IOI: mask = 0x4000; break;
|
||||||
|
case RB_AIM: mask = 0x0001; break;
|
||||||
|
case RB_FLA: mask = 0x0002; break;
|
||||||
|
case RB_AES: mask = 0x0004; break;
|
||||||
|
case RB_SHA: mask = 0x0008; break;
|
||||||
|
case RB_EHCI: mask = 0x0010; break;
|
||||||
|
case RB_OHCI0: mask = 0x0020; break;
|
||||||
|
case RB_OHCI1: mask = 0x0040; break;
|
||||||
|
case RB_SD0: mask = 0x0080; break;
|
||||||
|
case RB_SD1: mask = 0x0100; break;
|
||||||
|
default:
|
||||||
|
// ahb_flush_to() does this now
|
||||||
|
//DEBUG("_ahb_flush_to(%d): Invalid device\n", dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE: 0xd8b000x, not 0xd8b400x!
|
||||||
|
if(read32(0xd8b0008) & mask) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(dev) {
|
||||||
|
case RB_FLA:
|
||||||
|
case RB_AES:
|
||||||
|
case RB_SHA:
|
||||||
|
case RB_EHCI:
|
||||||
|
case RB_OHCI0:
|
||||||
|
case RB_OHCI1:
|
||||||
|
case RB_SD0:
|
||||||
|
case RB_SD1:
|
||||||
|
while((read32(LT_BOOT0) & 0xF) == 9)
|
||||||
|
set32(LT_COMPAT_MEMCTRL_WORKAROUND, 0x10000);
|
||||||
|
clear32(LT_COMPAT_MEMCTRL_WORKAROUND, 0x10000);
|
||||||
|
set32(LT_COMPAT_MEMCTRL_WORKAROUND, 0x2000000);
|
||||||
|
mask32(LT_AHB_UNK124, 0x7C0, 0x280);
|
||||||
|
set32(LT_AHB_UNK134, 0x400);
|
||||||
|
while((read32(LT_BOOT0) & 0xF) != 9);
|
||||||
|
set32(LT_AHB_UNK100, 0x400);
|
||||||
|
set32(LT_AHB_UNK104, 0x400);
|
||||||
|
set32(LT_AHB_UNK108, 0x400);
|
||||||
|
set32(LT_AHB_UNK10C, 0x400);
|
||||||
|
set32(LT_AHB_UNK110, 0x400);
|
||||||
|
set32(LT_AHB_UNK114, 0x400);
|
||||||
|
set32(LT_AHB_UNK118, 0x400);
|
||||||
|
set32(LT_AHB_UNK11C, 0x400);
|
||||||
|
set32(LT_AHB_UNK120, 0x400);
|
||||||
|
clear32(0xd8b0008, mask);
|
||||||
|
set32(0xd8b0008, mask);
|
||||||
|
clear32(LT_AHB_UNK134, 0x400);
|
||||||
|
clear32(LT_AHB_UNK100, 0x400);
|
||||||
|
clear32(LT_AHB_UNK104, 0x400);
|
||||||
|
clear32(LT_AHB_UNK108, 0x400);
|
||||||
|
clear32(LT_AHB_UNK10C, 0x400);
|
||||||
|
clear32(LT_AHB_UNK110, 0x400);
|
||||||
|
clear32(LT_AHB_UNK114, 0x400);
|
||||||
|
clear32(LT_AHB_UNK118, 0x400);
|
||||||
|
clear32(LT_AHB_UNK11C, 0x400);
|
||||||
|
clear32(LT_AHB_UNK120, 0x400);
|
||||||
|
clear32(LT_COMPAT_MEMCTRL_WORKAROUND, 0x2000000);
|
||||||
|
mask32(LT_AHB_UNK124, 0x7C0, 0xC0);
|
||||||
|
break;
|
||||||
|
case RB_IOD:
|
||||||
|
case RB_IOI:
|
||||||
|
set32(0xd8b0008, mask);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalidate device and then starlet
|
||||||
|
void ahb_flush_to(enum rb_client dev)
|
||||||
|
{
|
||||||
|
u32 mask;
|
||||||
|
switch(dev) {
|
||||||
|
case RB_IOD: mask = 0x8000; break;
|
||||||
|
case RB_IOI: mask = 0x4000; break;
|
||||||
|
case RB_AIM: mask = 0x0001; break;
|
||||||
|
case RB_FLA: mask = 0x0002; break;
|
||||||
|
case RB_AES: mask = 0x0004; break;
|
||||||
|
case RB_SHA: mask = 0x0008; break;
|
||||||
|
case RB_EHCI: mask = 0x0010; break;
|
||||||
|
case RB_OHCI0: mask = 0x0020; break;
|
||||||
|
case RB_OHCI1: mask = 0x0040; break;
|
||||||
|
case RB_SD0: mask = 0x0080; break;
|
||||||
|
case RB_SD1: mask = 0x0100; break;
|
||||||
|
case RB_SD2: mask = 0x10000; break;
|
||||||
|
case RB_SD3: mask = 0x20000; break;
|
||||||
|
case RB_EHC1: mask = 0x40000; break;
|
||||||
|
case RB_OHCI10: mask = 0x80000; break;
|
||||||
|
case RB_EHC2: mask = 0x100000; break;
|
||||||
|
case RB_OHCI20: mask = 0x200000; break;
|
||||||
|
case RB_SATA: mask = 0x400000; break;
|
||||||
|
case RB_AESS: mask = 0x800000; break;
|
||||||
|
case RB_SHAS: mask = 0x1000000; break;
|
||||||
|
default:
|
||||||
|
DEBUG("ahb_flush_to(%d): Invalid device\n", dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
|
||||||
|
write32(AHMN_RDBI_MASK, mask);
|
||||||
|
|
||||||
|
_ahb_flush_to(dev);
|
||||||
|
if(dev != RB_IOD)
|
||||||
|
_ahb_flush_to(RB_IOD);
|
||||||
|
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush device and also invalidate memory
|
||||||
|
void ahb_flush_from(enum wb_client dev)
|
||||||
|
{
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
u16 req = 0;
|
||||||
|
bool done = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch(dev)
|
||||||
|
{
|
||||||
|
case WB_IOD:
|
||||||
|
req = 0b0001;
|
||||||
|
break;
|
||||||
|
case WB_AIM:
|
||||||
|
case WB_EHCI:
|
||||||
|
case WB_EHC1:
|
||||||
|
case WB_EHC2:
|
||||||
|
case WB_SATA:
|
||||||
|
case WB_DMAB:
|
||||||
|
req = 0b0100;
|
||||||
|
break;
|
||||||
|
case WB_FLA:
|
||||||
|
case WB_OHCI0:
|
||||||
|
case WB_OHCI1:
|
||||||
|
case WB_SD0:
|
||||||
|
case WB_SD1:
|
||||||
|
case WB_SD2:
|
||||||
|
case WB_SD3:
|
||||||
|
case WB_OHCI10:
|
||||||
|
case WB_OHCI20:
|
||||||
|
case WB_DMAC:
|
||||||
|
req = 0b1000;
|
||||||
|
break;
|
||||||
|
case WB_AES:
|
||||||
|
case WB_SHA:
|
||||||
|
case WB_AESS:
|
||||||
|
case WB_SHAS:
|
||||||
|
case WB_DMAA:
|
||||||
|
req = 0b0010;
|
||||||
|
break;
|
||||||
|
case WB_ALL:
|
||||||
|
req = 0b1111;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DEBUG("ahb_flush(%d): Invalid device\n", dev);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
write16(MEM_FLUSH_MASK, req);
|
||||||
|
|
||||||
|
for(i = 0; i < 1000000; i++) {
|
||||||
|
if(!(read16(MEM_FLUSH_MASK) & req)) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
udelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!done) {
|
||||||
|
DEBUG("ahb_flush(%d): Flush (0x%x) did not ack!\n", dev, req);
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dc_flushrange(const void *start, u32 size)
|
||||||
|
{
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
if(size > 0x4000) {
|
||||||
|
_dc_flush();
|
||||||
|
} else {
|
||||||
|
void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE);
|
||||||
|
start = ALIGN_BACKWARD(start, LINESIZE);
|
||||||
|
_dc_flush_entries(start, (end - start) / LINESIZE);
|
||||||
|
}
|
||||||
|
_drain_write_buffer();
|
||||||
|
ahb_flush_from(WB_AIM);
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dc_invalidaterange(void *start, u32 size)
|
||||||
|
{
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE);
|
||||||
|
start = ALIGN_BACKWARD(start, LINESIZE);
|
||||||
|
_dc_inval_entries(start, (end - start) / LINESIZE);
|
||||||
|
ahb_flush_to(RB_IOD);
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dc_flushall(void)
|
||||||
|
{
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
_dc_flush();
|
||||||
|
_drain_write_buffer();
|
||||||
|
ahb_flush_from(WB_AIM);
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ic_invalidateall(void)
|
||||||
|
{
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
_ic_inval();
|
||||||
|
ahb_flush_to(RB_IOD);
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mem_protect(int enable, void *start, void *end)
|
||||||
|
{
|
||||||
|
write16(MEM_PROT, enable?1:0);
|
||||||
|
write16(MEM_PROT_START, (((u32)start) & 0xFFFFFFF) >> 12);
|
||||||
|
write16(MEM_PROT_END, (((u32)end) & 0xFFFFFFF) >> 12);
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mem_setswap(int enable)
|
||||||
|
{
|
||||||
|
u32 d = read32(LT_MEMIRR);
|
||||||
|
|
||||||
|
if((d & 0x20) && !enable)
|
||||||
|
write32(LT_MEMIRR, d & ~0x20);
|
||||||
|
if((!(d & 0x20)) && enable)
|
||||||
|
write32(LT_MEMIRR, d | 0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LOADER
|
||||||
|
u32 dma_addr(void *p)
|
||||||
|
{
|
||||||
|
u32 addr = (u32)p;
|
||||||
|
|
||||||
|
switch(addr>>20) {
|
||||||
|
case 0xfff:
|
||||||
|
case 0x0d4:
|
||||||
|
case 0x0dc:
|
||||||
|
if(read32(LT_MEMIRR) & 0x20) {
|
||||||
|
addr ^= 0x10000;
|
||||||
|
}
|
||||||
|
addr &= 0x0001FFFF;
|
||||||
|
addr |= 0x0d400000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//DEBUG("DMA to %p: address %08x\n", p, addr);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SECTION 0x012
|
||||||
|
|
||||||
|
#define NONBUFFERABLE 0x000
|
||||||
|
#define BUFFERABLE 0x004
|
||||||
|
#define WRITETHROUGH_CACHE 0x008
|
||||||
|
#define WRITEBACK_CACHE 0x00C
|
||||||
|
|
||||||
|
#define DOMAIN(x) ((x)<<5)
|
||||||
|
|
||||||
|
#define AP_ROM 0x000
|
||||||
|
#define AP_NOUSER 0x400
|
||||||
|
#define AP_ROUSER 0x800
|
||||||
|
#define AP_RWUSER 0xC00
|
||||||
|
|
||||||
|
// from, to, size: units of 1MB
|
||||||
|
void map_section(u32 from, u32 to, u32 size, u32 attributes)
|
||||||
|
{
|
||||||
|
attributes |= SECTION;
|
||||||
|
while(size--) {
|
||||||
|
__page_table[from++] = (to++<<20) | attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#define NO_CACHES
|
||||||
|
|
||||||
|
void mem_initialize(void)
|
||||||
|
{
|
||||||
|
u32 cr;
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
|
||||||
|
DEBUG("MEM: cleaning up\n");
|
||||||
|
|
||||||
|
_ic_inval();
|
||||||
|
_dc_inval();
|
||||||
|
_tlb_inval();
|
||||||
|
|
||||||
|
DEBUG("MEM: unprotecting memory\n");
|
||||||
|
|
||||||
|
mem_protect(0, NULL, NULL);
|
||||||
|
|
||||||
|
DEBUG("MEM: configuring heap\n");
|
||||||
|
|
||||||
|
extern char* fake_heap_start;
|
||||||
|
extern char* fake_heap_end;
|
||||||
|
extern char heap_start __asm__("__heap_start__");
|
||||||
|
extern char heap_end __asm__("__heap_end__");
|
||||||
|
|
||||||
|
fake_heap_start = &heap_start;
|
||||||
|
fake_heap_end = &heap_end;
|
||||||
|
|
||||||
|
DEBUG("MEM: mapping sections\n");
|
||||||
|
|
||||||
|
__page_table = (u32 *)memalign(16384, 16384);
|
||||||
|
memset32(__page_table, 0, sizeof(__page_table));
|
||||||
|
|
||||||
|
map_section(0x080, 0x080, 0x003, WRITEBACK_CACHE | DOMAIN(0) | AP_RWUSER); // MEM0
|
||||||
|
map_section(0x000, 0x000, 0x020, WRITEBACK_CACHE | DOMAIN(0) | AP_RWUSER); // MEM1
|
||||||
|
map_section(0x100, 0x100, 0xC00, WRITEBACK_CACHE | DOMAIN(0) | AP_RWUSER); // MEM2
|
||||||
|
map_section(0xFFF, 0xFFF, 0x001, WRITEBACK_CACHE | DOMAIN(0) | AP_RWUSER); // SRAM
|
||||||
|
|
||||||
|
map_section(0x0D0, 0x0D0, 0x010, NONBUFFERABLE | DOMAIN(0) | AP_RWUSER); // MMIO
|
||||||
|
|
||||||
|
set_dacr(0xFFFFFFFF); //manager access for all domains, ignore AP
|
||||||
|
set_ttbr((u32)__page_table); //configure translation table
|
||||||
|
|
||||||
|
_drain_write_buffer();
|
||||||
|
|
||||||
|
cr = get_cr();
|
||||||
|
|
||||||
|
#ifndef NO_CACHES
|
||||||
|
DEBUG("MEM: enabling caches\n");
|
||||||
|
|
||||||
|
cr |= CR_DCACHE | CR_ICACHE;
|
||||||
|
set_cr(cr);
|
||||||
|
|
||||||
|
DEBUG("MEM: enabling MMU\n");
|
||||||
|
|
||||||
|
cr |= CR_MMU;
|
||||||
|
set_cr(cr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEBUG("MEM: init done\n");
|
||||||
|
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mem_shutdown(void)
|
||||||
|
{
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
_dc_flush();
|
||||||
|
_drain_write_buffer();
|
||||||
|
u32 cr = get_cr();
|
||||||
|
cr &= ~(CR_MMU | CR_DCACHE | CR_ICACHE); //disable ICACHE, DCACHE, MMU
|
||||||
|
set_cr(cr);
|
||||||
|
_ic_inval();
|
||||||
|
_dc_inval();
|
||||||
|
_tlb_inval();
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
#endif
|
156
stage2/memory.h
Normal file
156
stage2/memory.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MEMORY_H__
|
||||||
|
#define __MEMORY_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define ALIGN_FORWARD(x,align) \
|
||||||
|
((__typeof__(x))((((u32)(x)) + (align) - 1) & (~(align-1))))
|
||||||
|
|
||||||
|
#define ALIGN_BACKWARD(x,align) \
|
||||||
|
((__typeof__(x))(((u32)(x)) & (~(align-1))))
|
||||||
|
|
||||||
|
enum rb_client {
|
||||||
|
RB_IOD = 0,
|
||||||
|
RB_IOI = 1,
|
||||||
|
RB_AIM = 2,
|
||||||
|
RB_FLA = 3,
|
||||||
|
RB_AES = 4,
|
||||||
|
RB_SHA = 5,
|
||||||
|
RB_EHCI = 6,
|
||||||
|
RB_OHCI0 = 7,
|
||||||
|
RB_OHCI1 = 8,
|
||||||
|
RB_SD0 = 9,
|
||||||
|
RB_SD1 = 10,
|
||||||
|
RB_SD2 = 11,
|
||||||
|
RB_SD3 = 12,
|
||||||
|
RB_EHC1 = 13,
|
||||||
|
RB_OHCI10 = 14,
|
||||||
|
RB_EHC2 = 15,
|
||||||
|
RB_OHCI20 = 16,
|
||||||
|
RB_SATA = 17,
|
||||||
|
RB_AESS = 18,
|
||||||
|
RB_SHAS = 19
|
||||||
|
};
|
||||||
|
|
||||||
|
enum wb_client {
|
||||||
|
WB_IOD = 0,
|
||||||
|
WB_AIM = 1,
|
||||||
|
WB_FLA = 2,
|
||||||
|
WB_AES = 3,
|
||||||
|
WB_SHA = 4,
|
||||||
|
WB_EHCI = 5,
|
||||||
|
WB_OHCI0 = 6,
|
||||||
|
WB_OHCI1 = 7,
|
||||||
|
WB_SD0 = 8,
|
||||||
|
WB_SD1 = 9,
|
||||||
|
WB_SD2 = 10,
|
||||||
|
WB_SD3 = 11,
|
||||||
|
WB_EHC1 = 12,
|
||||||
|
WB_OHCI10 = 13,
|
||||||
|
WB_EHC2 = 14,
|
||||||
|
WB_OHCI20 = 15,
|
||||||
|
WB_SATA = 16,
|
||||||
|
WB_AESS = 17,
|
||||||
|
WB_SHAS = 18,
|
||||||
|
WB_DMAA = 19,
|
||||||
|
WB_DMAB = 20,
|
||||||
|
WB_DMAC = 21,
|
||||||
|
WB_ALL = 22
|
||||||
|
};
|
||||||
|
|
||||||
|
void dc_flushrange(const void *start, u32 size);
|
||||||
|
void dc_invalidaterange(void *start, u32 size);
|
||||||
|
void dc_flushall(void);
|
||||||
|
void ic_invalidateall(void);
|
||||||
|
void ahb_flush_from(enum wb_client dev);
|
||||||
|
void ahb_flush_to(enum rb_client dev);
|
||||||
|
void mem_protect(int enable, void *start, void *end);
|
||||||
|
void mem_setswap(int enable);
|
||||||
|
|
||||||
|
void mem_initialize(void);
|
||||||
|
void mem_shutdown(void);
|
||||||
|
|
||||||
|
u32 dma_addr(void *);
|
||||||
|
|
||||||
|
static inline u32 get_cr(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrc\tp15, 0, %0, c1, c0, 0" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 get_ttbr(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrc\tp15, 0, %0, c2, c0, 0" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 get_dacr(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrc\tp15, 0, %0, c3, c0, 0" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_cr(u32 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ( "mcr\tp15, 0, %0, c1, c0, 0" :: "r" (data) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_ttbr(u32 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ( "mcr\tp15, 0, %0, c2, c0, 0" :: "r" (data) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_dacr(u32 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ( "mcr\tp15, 0, %0, c3, c0, 0" :: "r" (data) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 get_dfsr(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrc\tp15, 0, %0, c5, c0, 0" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 get_ifsr(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrc\tp15, 0, %0, c5, c0, 1" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 get_far(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrc\tp15, 0, %0, c6, c0, 0" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _ahb_flush_to(enum rb_client dev);
|
||||||
|
|
||||||
|
static inline void dc_inval_block_fast(void *block)
|
||||||
|
{
|
||||||
|
__asm__ volatile ( "mcr\tp15, 0, %0, c7, c6, 1" :: "r" (block) );
|
||||||
|
_ahb_flush_to(RB_IOD); //TODO: check if really needed and if not, remove
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dc_flush_block_fast(void *block)
|
||||||
|
{
|
||||||
|
__asm__ volatile ( "mcr\tp15, 0, %0, c7, c10, 1" :: "r" (block) );
|
||||||
|
__asm__ volatile ( "mcr\tp15, 0, %0, c7, c10, 4" :: "r" (0) );
|
||||||
|
ahb_flush_from(WB_AIM); //TODO: check if really needed and if not, remove
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
59
stage2/memory_asm.S
Normal file
59
stage2/memory_asm.S
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
.arm
|
||||||
|
|
||||||
|
.globl _dc_inval_entries
|
||||||
|
.globl _dc_flush_entries
|
||||||
|
.globl _dc_flush
|
||||||
|
.globl _dc_inval
|
||||||
|
.globl _ic_inval
|
||||||
|
.globl _drain_write_buffer
|
||||||
|
.globl _tlb_inval
|
||||||
|
|
||||||
|
.section .sram.text
|
||||||
|
|
||||||
|
_dc_inval_entries:
|
||||||
|
mcr p15, 0, r0, c7, c6, 1
|
||||||
|
add r0, #0x20
|
||||||
|
subs r1, #1
|
||||||
|
bne _dc_inval_entries
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
_dc_flush_entries:
|
||||||
|
mcr p15, 0, r0, c7, c10, 1
|
||||||
|
add r0, #0x20
|
||||||
|
subs r1, #1
|
||||||
|
bne _dc_flush_entries
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
_dc_flush:
|
||||||
|
mrc p15, 0, pc, c7, c10, 3
|
||||||
|
bne _dc_flush
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
_dc_inval:
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c7, c6, 0
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
_ic_inval:
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c7, c5, 0
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
_drain_write_buffer:
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c7, c10, 4
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
_tlb_inval:
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c8, c7
|
||||||
|
bx lr
|
456
stage2/nand.c
Normal file
456
stage2/nand.c
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 rw-r-r-0644
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nand.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ECC definitions */
|
||||||
|
#define ECC_SIZE 0x10
|
||||||
|
#define ECC_STOR_OFFS 0x30
|
||||||
|
#define ECC_CALC_OFFS 0x40
|
||||||
|
|
||||||
|
/* required buffers sizes */
|
||||||
|
#define SPARE_BUF_SIZE (SPARE_SIZE + ECC_SIZE + 0x10)
|
||||||
|
#define STATUS_BUF_SIZE 0x40
|
||||||
|
|
||||||
|
/* NAND chip commands */
|
||||||
|
#define CMD_CHIPID 0x90
|
||||||
|
#define CMD_RESET 0xff
|
||||||
|
#define CMD_GET_STATUS 0x70
|
||||||
|
#define CMD_ERASE_SETUP 0x60
|
||||||
|
#define CMD_ERASE 0xd0
|
||||||
|
#define CMD_SERIALDATA_IN 0x80
|
||||||
|
#define CMD_RANDOMDATA_IN 0x85
|
||||||
|
#define CMD_PROGRAM 0x10
|
||||||
|
#define CMD_READ_SETUP 0x00
|
||||||
|
#define CMD_READ 0x30
|
||||||
|
|
||||||
|
/* NAND_CTRL definitions */
|
||||||
|
#define CTRL_FL_EXEC (0x80000000)
|
||||||
|
#define CTRL_FL_ERR (0x20000000)
|
||||||
|
#define CTRL_FL_IRQ (0x40000000)
|
||||||
|
#define CTRL_FL_WAIT (0x00008000)
|
||||||
|
#define CTRL_FL_WR (0x00004000)
|
||||||
|
#define CTRL_FL_RD (0x00002000)
|
||||||
|
#define CTRL_FL_ECC (0x00001000)
|
||||||
|
#define CTRL_CMD(cmd) (0x00ff0000 & (cmd << 16))
|
||||||
|
#define CTRL_ADDR(addr) (0x1f000000 & (addr << 24))
|
||||||
|
#define CTRL_SIZE(size) (0x00000fff & (size))
|
||||||
|
|
||||||
|
/* NAND_CONF definitions */
|
||||||
|
#define CONF_FL_WP (0x80000000) /* bsp:fla clears this flag when writing */
|
||||||
|
#define CONF_FL_EN (0x08000000) /* enable nand controller */
|
||||||
|
#define CONF_ATTR_INIT (0x743e3eff) /* initial nand config */
|
||||||
|
#define CONF_ATTR_NORMAL (0x550f1eff) /* normal nand config */
|
||||||
|
|
||||||
|
/* NAND_BANK definitions */
|
||||||
|
#define BANK_FL_4 (0x00000004) /* set by bsp:fla for revisions after latte A2X */
|
||||||
|
|
||||||
|
#if NAND_WRITE_ENABLED
|
||||||
|
static u8 nand_status_buf[STATUS_BUF_SIZE] ALIGNED(256);
|
||||||
|
#endif
|
||||||
|
static u8 nand_spare_buf[SPARE_BUF_SIZE] ALIGNED(256);
|
||||||
|
|
||||||
|
static u32 nand_enabled_banks = BANK_SLC;
|
||||||
|
|
||||||
|
static int irq_flag = 0;
|
||||||
|
|
||||||
|
int nand_error(const char *error)
|
||||||
|
{
|
||||||
|
DEBUG("nand: %s\n", error);
|
||||||
|
nand_initialize();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_irq(void)
|
||||||
|
{
|
||||||
|
ahb_flush_from(WB_FLA);
|
||||||
|
ahb_flush_to(RB_IOD);
|
||||||
|
irq_flag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_irq_clear_and_enable(void)
|
||||||
|
{
|
||||||
|
irq_flag = 0;
|
||||||
|
irq_enable(IRQ_NAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_wait_irq(void)
|
||||||
|
{
|
||||||
|
while(!irq_flag) {
|
||||||
|
u32 cookie = irq_kill();
|
||||||
|
if (!irq_flag) {
|
||||||
|
irq_wait();
|
||||||
|
}
|
||||||
|
irq_restore(cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_enable_banks(u32 bank)
|
||||||
|
{
|
||||||
|
nand_enabled_banks = bank & 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_set_config(int write_enable)
|
||||||
|
{
|
||||||
|
u32 conf, bank;
|
||||||
|
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
write32(NAND_CONF, 0);
|
||||||
|
|
||||||
|
/* set nand config */
|
||||||
|
conf = (write_enable ? 0 : CONF_FL_WP)
|
||||||
|
| (CONF_FL_EN)
|
||||||
|
| (CONF_ATTR_NORMAL);
|
||||||
|
write32(NAND_CONF, conf);
|
||||||
|
|
||||||
|
/* set nand bank */
|
||||||
|
bank = BANK_FL_4
|
||||||
|
| nand_enabled_banks;
|
||||||
|
write32(NAND_BANK, bank);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NAND_WRITE_ENABLED
|
||||||
|
|
||||||
|
int nand_erase_block(u32 blockno)
|
||||||
|
{
|
||||||
|
if (blockno > BLOCK_COUNT) {
|
||||||
|
return nand_error("invalid block number");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear write protection */
|
||||||
|
nand_set_config(1);
|
||||||
|
|
||||||
|
/* erase setup */
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
write32(NAND_ADDR0, 0);
|
||||||
|
write32(NAND_ADDR1, blockno * BLOCK_PAGES);
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_CMD(CMD_ERASE_SETUP) |
|
||||||
|
CTRL_ADDR(0x1c));
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
nand_irq_clear_and_enable();
|
||||||
|
|
||||||
|
/* erase */
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_FL_IRQ |
|
||||||
|
CTRL_CMD(CMD_ERASE) |
|
||||||
|
CTRL_FL_WAIT);
|
||||||
|
nand_wait_irq();
|
||||||
|
|
||||||
|
/* set write protection */
|
||||||
|
nand_set_config(0);
|
||||||
|
|
||||||
|
/* get status */
|
||||||
|
*nand_status_buf = 1;
|
||||||
|
dc_flushrange(nand_status_buf, STATUS_BUF_SIZE);
|
||||||
|
|
||||||
|
write32(NAND_DATA, dma_addr(nand_status_buf));
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_CMD(CMD_GET_STATUS) |
|
||||||
|
CTRL_FL_RD |
|
||||||
|
CTRL_SIZE(STATUS_BUF_SIZE));
|
||||||
|
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
|
||||||
|
ahb_flush_from(WB_FLA);
|
||||||
|
dc_invalidaterange(nand_status_buf, STATUS_BUF_SIZE);
|
||||||
|
|
||||||
|
/* check failure */
|
||||||
|
if (*nand_status_buf & 1) {
|
||||||
|
return nand_error("erase command failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nand_write_page(u32 pageno, void *data, void *spare)
|
||||||
|
{
|
||||||
|
if (pageno > PAGE_COUNT) {
|
||||||
|
return nand_error("invalid page number");
|
||||||
|
}
|
||||||
|
if ((u32)data & 0x1f) {
|
||||||
|
return nand_error("unaligned page buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
dc_flushrange(data, PAGE_SIZE);
|
||||||
|
ahb_flush_to(RB_FLA);
|
||||||
|
dc_invalidaterange(nand_spare_buf + ECC_CALC_OFFS, ECC_SIZE);
|
||||||
|
|
||||||
|
/* clear write protection */
|
||||||
|
nand_set_config(1);
|
||||||
|
|
||||||
|
/* send page content and calc ecc */
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
write32(NAND_ADDR0, 0);
|
||||||
|
write32(NAND_ADDR1, pageno);
|
||||||
|
write32(NAND_DATA, dma_addr(data));
|
||||||
|
write32(NAND_ECC, dma_addr(nand_spare_buf));
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_ADDR(0x1f) |
|
||||||
|
CTRL_CMD(CMD_SERIALDATA_IN) |
|
||||||
|
CTRL_FL_WR |
|
||||||
|
CTRL_FL_ECC |
|
||||||
|
CTRL_SIZE(PAGE_SIZE));
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
|
||||||
|
if (read32(NAND_CTRL) & CTRL_FL_ERR) {
|
||||||
|
nand_error("error executing data input command");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare page spare */
|
||||||
|
ahb_flush_from(WB_FLA);
|
||||||
|
if (spare) {
|
||||||
|
memcpy(nand_spare_buf, spare, SPARE_SIZE);
|
||||||
|
} else {
|
||||||
|
memset(nand_spare_buf, 0, SPARE_SIZE);
|
||||||
|
}
|
||||||
|
nand_spare_buf[0] = 0xff;
|
||||||
|
memcpy(nand_spare_buf + ECC_STOR_OFFS, nand_spare_buf + ECC_CALC_OFFS, ECC_SIZE);
|
||||||
|
dc_flushrange(nand_spare_buf, SPARE_SIZE);
|
||||||
|
|
||||||
|
/* setup irq */
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
nand_irq_clear_and_enable();
|
||||||
|
|
||||||
|
/* send spare content */
|
||||||
|
write32(NAND_ADDR0, PAGE_SIZE);
|
||||||
|
write32(NAND_ADDR1, 0);
|
||||||
|
write32(NAND_DATA, dma_addr(nand_spare_buf));
|
||||||
|
write32(NAND_ECC, 0);
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_ADDR(0x3) |
|
||||||
|
CTRL_CMD(CMD_RANDOMDATA_IN) |
|
||||||
|
CTRL_FL_WR |
|
||||||
|
CTRL_SIZE(SPARE_SIZE));
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
|
||||||
|
if (read32(NAND_CTRL) & CTRL_FL_ERR) {
|
||||||
|
nand_error("error executing random data input command");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* program page */
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_FL_IRQ |
|
||||||
|
CTRL_CMD(CMD_PROGRAM) |
|
||||||
|
CTRL_FL_WAIT);
|
||||||
|
nand_wait_irq();
|
||||||
|
|
||||||
|
/* set write protection */
|
||||||
|
nand_set_config(0);
|
||||||
|
|
||||||
|
/* get status */
|
||||||
|
*nand_status_buf = 1;
|
||||||
|
dc_flushrange(nand_status_buf, STATUS_BUF_SIZE);
|
||||||
|
|
||||||
|
write32(NAND_DATA, dma_addr(nand_status_buf));
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_CMD(CMD_GET_STATUS) |
|
||||||
|
CTRL_FL_RD |
|
||||||
|
CTRL_SIZE(STATUS_BUF_SIZE));
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
|
||||||
|
ahb_flush_from(WB_FLA);
|
||||||
|
dc_invalidaterange(nand_status_buf, STATUS_BUF_SIZE);
|
||||||
|
|
||||||
|
/* check failure */
|
||||||
|
if (*nand_status_buf & 1) {
|
||||||
|
return nand_error("page program command failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int nand_ecc_correct(u8 *data, u32 *ecc_save, u32 *ecc_calc, u32 size)
|
||||||
|
{
|
||||||
|
u32 syndrome;
|
||||||
|
u16 odd, even;
|
||||||
|
|
||||||
|
/* check if the page contain ecc errors */
|
||||||
|
if (!memcmp(ecc_save, ecc_calc, size)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* correct ecc errors */
|
||||||
|
for (int i = 0; i < (size / 4); i++) {
|
||||||
|
if (ecc_save[i] == ecc_calc[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't try to correct unformatted pages */
|
||||||
|
if (ecc_save[i] == 0xffffffff) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate ecc syndrome */
|
||||||
|
syndrome = (ecc_save[i] ^ ecc_calc[i]) & 0x0fff0fff;
|
||||||
|
if ((syndrome & (syndrome - 1)) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract odd and even halves */
|
||||||
|
odd = syndrome >> 16;
|
||||||
|
even = syndrome;
|
||||||
|
|
||||||
|
/* uncorrectable error */
|
||||||
|
if ((odd ^ even) != 0xfff) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fix the bad bit */
|
||||||
|
data[i * 0x200 + (odd >> 3)] ^= 1 << (odd & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nand_read_page(u32 pageno, void *data, void *spare)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (pageno > PAGE_COUNT) {
|
||||||
|
return nand_error("invalid page number");
|
||||||
|
}
|
||||||
|
if ((u32)data & 0x1f) {
|
||||||
|
return nand_error("unaligned page buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set nand config */
|
||||||
|
nand_set_config(0);
|
||||||
|
|
||||||
|
/* prepare for reading */
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
write32(NAND_ADDR0, 0);
|
||||||
|
write32(NAND_ADDR1, pageno);
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_ADDR(0x1f) |
|
||||||
|
CTRL_CMD(CMD_READ_SETUP));
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
|
||||||
|
/* read page and spare */
|
||||||
|
dc_invalidaterange(data, PAGE_SIZE);
|
||||||
|
dc_invalidaterange(nand_spare_buf, SPARE_BUF_SIZE);
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
write32(NAND_DATA, dma_addr(data));
|
||||||
|
write32(NAND_ECC, dma_addr(nand_spare_buf));
|
||||||
|
nand_irq_clear_and_enable();
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_FL_IRQ |
|
||||||
|
CTRL_CMD(CMD_READ) |
|
||||||
|
CTRL_FL_WAIT |
|
||||||
|
CTRL_FL_RD |
|
||||||
|
CTRL_FL_ECC |
|
||||||
|
CTRL_SIZE(PAGE_SIZE + SPARE_SIZE));
|
||||||
|
nand_wait_irq();
|
||||||
|
|
||||||
|
if (read32(NAND_CTRL) & CTRL_FL_ERR) {
|
||||||
|
return nand_error("error executing page read command");
|
||||||
|
}
|
||||||
|
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
ahb_flush_from(WB_FLA);
|
||||||
|
|
||||||
|
/* correct ecc errors */
|
||||||
|
res = nand_ecc_correct(data,
|
||||||
|
(u32*)(nand_spare_buf + ECC_STOR_OFFS),
|
||||||
|
(u32*)(nand_spare_buf + ECC_CALC_OFFS),
|
||||||
|
ECC_SIZE);
|
||||||
|
if (res < 0) {
|
||||||
|
return nand_error("uncorrectable ecc error");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy spare from internal buffer */
|
||||||
|
if (spare) {
|
||||||
|
memcpy(spare, nand_spare_buf, SPARE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_deinitialize(void)
|
||||||
|
{
|
||||||
|
/* shutdown nand banks */
|
||||||
|
write32(NAND_BANK_CTRL, 0);
|
||||||
|
while(read32(NAND_BANK_CTRL) & (1 << 31));
|
||||||
|
write32(NAND_BANK_CTRL, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 0xc0; i += 0x18) {
|
||||||
|
write32(NAND_REG_BASE + 0x40 + i, 0);
|
||||||
|
write32(NAND_REG_BASE + 0x44 + i, 0);
|
||||||
|
write32(NAND_REG_BASE + 0x48 + i, 0);
|
||||||
|
write32(NAND_REG_BASE + 0x4c + i, 0);
|
||||||
|
write32(NAND_REG_BASE + 0x50 + i, 0);
|
||||||
|
write32(NAND_REG_BASE + 0x54 + i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shutdown main nand bank */
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
while(read32(NAND_CTRL) & (1 << 31));
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
|
||||||
|
/* write init config */
|
||||||
|
write32(NAND_CONF, CONF_ATTR_INIT);
|
||||||
|
write32(NAND_BANK, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_initialize(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
/* shutdown nand interface */
|
||||||
|
nand_deinitialize();
|
||||||
|
|
||||||
|
/* set nand init config and enable */
|
||||||
|
write32(NAND_CONF,
|
||||||
|
CONF_FL_EN |
|
||||||
|
CONF_ATTR_INIT);
|
||||||
|
|
||||||
|
/* set nand bank */
|
||||||
|
write32(NAND_BANK,
|
||||||
|
BANK_FL_4 | // ???
|
||||||
|
(i ? 3 : 1)); // ???
|
||||||
|
|
||||||
|
/* reset nand chip */
|
||||||
|
write32(NAND_CTRL,
|
||||||
|
CTRL_FL_EXEC |
|
||||||
|
CTRL_CMD(CMD_RESET) |
|
||||||
|
CTRL_FL_WAIT);
|
||||||
|
while(read32(NAND_CTRL) & CTRL_FL_EXEC);
|
||||||
|
write32(NAND_CTRL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set normal nand config */
|
||||||
|
nand_set_config(0);
|
||||||
|
}
|
64
stage2/nand.h
Normal file
64
stage2/nand.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 rw-r-r-0644
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NAND_H__
|
||||||
|
#define __NAND_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define NAND_WRITE_ENABLED 1
|
||||||
|
|
||||||
|
/* nand structure definitions */
|
||||||
|
#define PAGE_SIZE 0x800
|
||||||
|
#define PAGE_COUNT 0x40000
|
||||||
|
|
||||||
|
#define SPARE_SIZE 0x40
|
||||||
|
|
||||||
|
#define CLUSTER_PAGES 8
|
||||||
|
#define CLUSTER_SIZE (PAGE_SIZE * CLUSTER_PAGES)
|
||||||
|
#define CLUSTER_COUNT (PAGE_COUNT / CLUSTER_PAGES)
|
||||||
|
|
||||||
|
#define BLOCK_CLUSTERS 8
|
||||||
|
#define BLOCK_PAGES 0x40
|
||||||
|
#define BLOCK_SIZE (CLUSTER_SIZE * BLOCK_CLUSTERS)
|
||||||
|
#define BLOCK_COUNT 0x1000
|
||||||
|
|
||||||
|
/* nand banks */
|
||||||
|
#define BANK_SLCCMPT 1
|
||||||
|
#define BANK_SLC 2
|
||||||
|
|
||||||
|
/* initialize nand */
|
||||||
|
void nand_initialize(void);
|
||||||
|
|
||||||
|
/* shutdown nand interface */
|
||||||
|
void nand_deinitialize(void);
|
||||||
|
|
||||||
|
/* read page and spare */
|
||||||
|
int nand_read_page(u32 pageno, void *data, void *spare);
|
||||||
|
|
||||||
|
#if NAND_WRITE_ENABLED
|
||||||
|
/* write page and spare */
|
||||||
|
int nand_write_page(u32 pageno, void *data, void *spare);
|
||||||
|
|
||||||
|
/* erase a block of pages */
|
||||||
|
int nand_erase_block(u32 blockno);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* set enabled nand banks */
|
||||||
|
void nand_enable_banks(u32 bank);
|
||||||
|
|
||||||
|
/* nand irq handler */
|
||||||
|
void nand_irq(void);
|
||||||
|
|
||||||
|
#endif
|
713
stage2/sdcard.c
Normal file
713
stage2/sdcard.c
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bsdtypes.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "latte.h"
|
||||||
|
#include "sdhc.h"
|
||||||
|
#include "sdcard.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include "debug.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
|
||||||
|
#ifdef CAN_HAZ_IRQ
|
||||||
|
#include "irq.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#define SDCARD_DEBUG
|
||||||
|
|
||||||
|
#ifdef SDCARD_DEBUG
|
||||||
|
static int sdcarddebug = 2;
|
||||||
|
#define DPRINTF(n,s) do { if ((n) <= sdcarddebug) DEBUG(s); } while (0)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(n,s) do {} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct sdhc_host sdcard_host;
|
||||||
|
|
||||||
|
struct sdcard_ctx {
|
||||||
|
sdmmc_chipset_handle_t handle;
|
||||||
|
int inserted;
|
||||||
|
int sdhc_blockmode;
|
||||||
|
int selected;
|
||||||
|
int new_card; // set to 1 everytime a new card is inserted
|
||||||
|
|
||||||
|
u32 num_sectors;
|
||||||
|
u16 rca;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sdcard_ctx card;
|
||||||
|
|
||||||
|
void sdcard_attach(sdmmc_chipset_handle_t handle)
|
||||||
|
{
|
||||||
|
memset(&card, 0, sizeof(card));
|
||||||
|
|
||||||
|
card.handle = handle;
|
||||||
|
|
||||||
|
DPRINTF(0, ("sdcard: attached new SD/MMC card\n"));
|
||||||
|
|
||||||
|
sdhc_host_reset(card.handle);
|
||||||
|
|
||||||
|
if (sdhc_card_detect(card.handle)) {
|
||||||
|
DPRINTF(1, ("card is inserted. starting init sequence.\n"));
|
||||||
|
sdcard_needs_discover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdcard_abort(void) {
|
||||||
|
struct sdmmc_command cmd;
|
||||||
|
DEBUG("sdcard: abortion kthx\n");
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_STOP_TRANSMISSION;
|
||||||
|
cmd.c_arg = 0;
|
||||||
|
cmd.c_flags = SCF_RSP_R1B;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdcard_needs_discover(void)
|
||||||
|
{
|
||||||
|
struct sdmmc_command cmd;
|
||||||
|
u32 ocr = card.handle->ocr;
|
||||||
|
|
||||||
|
DPRINTF(0, ("sdcard: card needs discovery.\n"));
|
||||||
|
sdhc_host_reset(card.handle);
|
||||||
|
card.new_card = 1;
|
||||||
|
|
||||||
|
if (!sdhc_card_detect(card.handle)) {
|
||||||
|
DPRINTF(1, ("sdcard: card (no longer?) inserted.\n"));
|
||||||
|
card.inserted = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(1, ("sdcard: enabling power\n"));
|
||||||
|
if (sdhc_bus_power(card.handle, ocr) != 0) {
|
||||||
|
DEBUG("sdcard: powerup failed for card\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(1, ("sdcard: enabling clock\n"));
|
||||||
|
if (sdhc_bus_clock(card.handle, SDMMC_SDCLK_25MHZ, SDMMC_TIMING_LEGACY) != 0) {
|
||||||
|
DEBUG("sdcard: could not enable clock for card\n");
|
||||||
|
goto out_power;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhc_bus_width(card.handle, 1);
|
||||||
|
|
||||||
|
DPRINTF(1, ("sdcard: sending GO_IDLE_STATE\n"));
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_GO_IDLE_STATE;
|
||||||
|
cmd.c_flags = SCF_RSP_R0;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: GO_IDLE_STATE failed with %d\n", cmd.c_error);
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
DPRINTF(2, ("sdcard: GO_IDLE_STATE response: %x\n", MMC_R1(cmd.c_resp)));
|
||||||
|
|
||||||
|
DPRINTF(1, ("sdcard: sending SEND_IF_COND\n"));
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = SD_SEND_IF_COND;
|
||||||
|
cmd.c_arg = 0x1aa;
|
||||||
|
cmd.c_flags = SCF_RSP_R7;
|
||||||
|
cmd.c_timeout = 100;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error || (cmd.c_resp[0] & 0xff) != 0xaa)
|
||||||
|
ocr &= ~SD_OCR_SDHC_CAP;
|
||||||
|
else
|
||||||
|
ocr |= SD_OCR_SDHC_CAP;
|
||||||
|
DPRINTF(2, ("sdcard: SEND_IF_COND ocr: %x\n", ocr));
|
||||||
|
|
||||||
|
int tries;
|
||||||
|
for (tries = 100; tries > 0; tries--) {
|
||||||
|
udelay(100000);
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_APP_CMD;
|
||||||
|
cmd.c_arg = 0;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_APP_CMD failed with %d\n", cmd.c_error);
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = SD_APP_OP_COND;
|
||||||
|
cmd.c_arg = ocr;
|
||||||
|
cmd.c_flags = SCF_RSP_R3;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: SD_APP_OP_COND failed with %d\n", cmd.c_error);
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(3, ("sdcard: response for SEND_IF_COND: %08x\n",
|
||||||
|
MMC_R1(cmd.c_resp)));
|
||||||
|
if (ISSET(MMC_R1(cmd.c_resp), MMC_OCR_MEM_READY))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!ISSET(cmd.c_resp[0], MMC_OCR_MEM_READY)) {
|
||||||
|
DEBUG("sdcard: card failed to powerup.\n");
|
||||||
|
goto out_power;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ISSET(MMC_R1(cmd.c_resp), SD_OCR_SDHC_CAP))
|
||||||
|
card.sdhc_blockmode = 1;
|
||||||
|
else
|
||||||
|
card.sdhc_blockmode = 0;
|
||||||
|
DPRINTF(2, ("sdcard: SDHC: %d\n", card.sdhc_blockmode));
|
||||||
|
|
||||||
|
u8 *resp;
|
||||||
|
u32 *resp32;
|
||||||
|
|
||||||
|
DPRINTF(2, ("sdcard: MMC_ALL_SEND_CID\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_ALL_SEND_CID;
|
||||||
|
cmd.c_arg = 0;
|
||||||
|
cmd.c_flags = SCF_RSP_R2;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_ALL_SEND_CID failed with %d\n", cmd.c_error);
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = (u8 *)cmd.c_resp;
|
||||||
|
resp32 = (u32 *)cmd.c_resp;
|
||||||
|
DEBUG("CID: %08lX%08lX%08lX%08lX\n", resp32[0], resp32[1], resp32[2], resp32[3]);
|
||||||
|
DEBUG("CID: mid=%02x name='%c%c%c%c%c%c%c' prv=%d.%d psn=%02x%02x%02x%02x mdt=%d/%d\n", resp[14],
|
||||||
|
resp[13],resp[12],resp[11],resp[10],resp[9],resp[8],resp[7], resp[6], resp[5] >> 4, resp[5] & 0xf,
|
||||||
|
resp[4], resp[3], resp[2], resp[0] & 0xf, 2000 + (resp[0] >> 4));
|
||||||
|
|
||||||
|
DPRINTF(2, ("sdcard: SD_SEND_RELATIVE_ADDRESS\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = SD_SEND_RELATIVE_ADDR;
|
||||||
|
cmd.c_arg = 0;
|
||||||
|
cmd.c_flags = SCF_RSP_R6;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: SD_SEND_RCA failed with %d\n", cmd.c_error);
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
card.rca = MMC_R1(cmd.c_resp)>>16;
|
||||||
|
DPRINTF(2, ("sdcard: rca: %08x\n", card.rca));
|
||||||
|
|
||||||
|
card.selected = 0;
|
||||||
|
card.inserted = 1;
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_SEND_CSD;
|
||||||
|
cmd.c_arg = ((u32)card.rca)<<16;
|
||||||
|
cmd.c_flags = SCF_RSP_R2;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_SEND_CSD failed with %d\n", cmd.c_error);
|
||||||
|
goto out_power;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = (u8 *)cmd.c_resp;
|
||||||
|
resp32 = (u32 *)cmd.c_resp;
|
||||||
|
DEBUG("CSD: %08lX%08lX%08lX%08lX\n", resp32[0], resp32[1], resp32[2], resp32[3]);
|
||||||
|
|
||||||
|
if (resp[13] == 0xe) { // sdhc
|
||||||
|
unsigned int c_size = resp[7] << 16 | resp[6] << 8 | resp[5];
|
||||||
|
DEBUG("sdcard: sdhc mode, c_size=%u, card size = %uk\n", c_size, (c_size + 1)* 512);
|
||||||
|
card.num_sectors = (c_size + 1) * 1024; // number of 512-byte sectors
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned int taac, nsac, read_bl_len, c_size, c_size_mult;
|
||||||
|
taac = resp[13];
|
||||||
|
nsac = resp[12];
|
||||||
|
read_bl_len = resp[9] & 0xF;
|
||||||
|
|
||||||
|
c_size = (resp[8] & 3) << 10;
|
||||||
|
c_size |= (resp[7] << 2);
|
||||||
|
c_size |= (resp[6] >> 6);
|
||||||
|
c_size_mult = (resp[5] & 3) << 1;
|
||||||
|
c_size_mult |= resp[4] >> 7;
|
||||||
|
DEBUG("taac=%u nsac=%u read_bl_len=%u c_size=%u c_size_mult=%u card size=%u bytes\n",
|
||||||
|
taac, nsac, read_bl_len, c_size, c_size_mult, (c_size + 1) * (4 << c_size_mult) * (1 << read_bl_len));
|
||||||
|
card.num_sectors = (c_size + 1) * (4 << c_size_mult) * (1 << read_bl_len) / 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdcard_select();
|
||||||
|
|
||||||
|
DPRINTF(2, ("mlc: MMC_SEND_STATUS\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_SEND_STATUS;
|
||||||
|
cmd.c_arg = ((u32)card.rca)<<16;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("mlc: MMC_SEND_STATUS failed with %d\n", cmd.c_error);
|
||||||
|
card.inserted = card.selected = 0;
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(2, ("sdcard: MMC_SET_BLOCKLEN\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_SET_BLOCKLEN;
|
||||||
|
cmd.c_arg = SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_SET_BLOCKLEN failed with %d\n", cmd.c_error);
|
||||||
|
card.inserted = card.selected = 0;
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(2, ("sdcard: MMC_APP_CMD\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_APP_CMD;
|
||||||
|
cmd.c_arg = ((u32)card.rca)<<16;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_APP_CMD failed with %d\n", cmd.c_error);
|
||||||
|
card.inserted = card.selected = 0;
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(2, ("sdcard: SD_APP_SET_BUS_WIDTH\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = SD_APP_SET_BUS_WIDTH;
|
||||||
|
cmd.c_arg = SD_ARG_BUS_WIDTH_4;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: SD_APP_SET_BUS_WIDTH failed with %d\n", cmd.c_error);
|
||||||
|
card.inserted = card.selected = 0;
|
||||||
|
goto out_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhc_bus_width(card.handle, 4);
|
||||||
|
|
||||||
|
DPRINTF(1, ("sdcard: enabling clock\n"));
|
||||||
|
if (sdhc_bus_clock(card.handle, SDMMC_SDCLK_25MHZ, SDMMC_TIMING_LEGACY) != 0) {
|
||||||
|
DEBUG("sdcard: could not enable clock for card\n");
|
||||||
|
goto out_power;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
out_clock:
|
||||||
|
sdhc_bus_width(card.handle, 1);
|
||||||
|
sdhc_bus_clock(card.handle, SDMMC_SDCLK_OFF, SDMMC_TIMING_LEGACY);
|
||||||
|
|
||||||
|
out_power:
|
||||||
|
sdhc_bus_power(card.handle, 0);
|
||||||
|
out:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sdcard_select(void)
|
||||||
|
{
|
||||||
|
struct sdmmc_command cmd;
|
||||||
|
|
||||||
|
DPRINTF(2, ("sdcard: MMC_SELECT_CARD\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_SELECT_CARD;
|
||||||
|
cmd.c_arg = ((u32)card.rca)<<16;
|
||||||
|
cmd.c_flags = SCF_RSP_R1B;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
DEBUG("%s: resp=%lx\n", __FUNCTION__, MMC_R1(cmd.c_resp));
|
||||||
|
// sdhc_dump_regs(card.handle);
|
||||||
|
|
||||||
|
// DEBUG("present state = %x\n", HREAD4(hp, SDHC_PRESENT_STATE));
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_SELECT card failed with %d.\n", cmd.c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
card.selected = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_check_card(void)
|
||||||
|
{
|
||||||
|
if (card.inserted == 0)
|
||||||
|
return SDMMC_NO_CARD;
|
||||||
|
|
||||||
|
if (card.new_card == 1)
|
||||||
|
return SDMMC_NEW_CARD;
|
||||||
|
|
||||||
|
return SDMMC_INSERTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_ack_card(void)
|
||||||
|
{
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
card.new_card = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_start_read(u32 blk_start, u32 blk_count, void *data, struct sdmmc_command* cmdbuf)
|
||||||
|
{
|
||||||
|
// DEBUG("%s(%u, %u, %p)\n", __FUNCTION__, blk_start, blk_count, data);
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: READ: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.selected == 0) {
|
||||||
|
if (sdcard_select() < 0) {
|
||||||
|
DEBUG("sdcard: READ: cannot select card.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(cmdbuf, 0, sizeof(struct sdmmc_command));
|
||||||
|
|
||||||
|
if(blk_count > 1) {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_READ_BLOCK_MULTIPLE\n"));
|
||||||
|
cmdbuf->c_opcode = MMC_READ_BLOCK_MULTIPLE;
|
||||||
|
} else {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_READ_BLOCK_SINGLE\n"));
|
||||||
|
cmdbuf->c_opcode = MMC_READ_BLOCK_SINGLE;
|
||||||
|
}
|
||||||
|
if (card.sdhc_blockmode)
|
||||||
|
cmdbuf->c_arg = blk_start;
|
||||||
|
else
|
||||||
|
cmdbuf->c_arg = blk_start * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmdbuf->c_data = data;
|
||||||
|
cmdbuf->c_datalen = blk_count * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmdbuf->c_blklen = SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmdbuf->c_flags = SCF_RSP_R1 | SCF_CMD_READ;
|
||||||
|
sdhc_async_command(card.handle, cmdbuf);
|
||||||
|
|
||||||
|
if (cmdbuf->c_error) {
|
||||||
|
DEBUG("sdcard: MMC_READ_BLOCK_%s failed with %d\n", blk_count > 1 ? "MULTIPLE" : "SINGLE", cmdbuf->c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(blk_count > 1)
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_READ_BLOCK_MULTIPLE started\n"));
|
||||||
|
else
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_READ_BLOCK_SINGLE started\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_end_read(struct sdmmc_command* cmdbuf)
|
||||||
|
{
|
||||||
|
// DEBUG("%s(%u, %u, %p)\n", __FUNCTION__, blk_start, blk_count, data);
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: READ: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.selected == 0) {
|
||||||
|
if (sdcard_select() < 0) {
|
||||||
|
DEBUG("sdcard: READ: cannot select card.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhc_async_response(card.handle, cmdbuf);
|
||||||
|
|
||||||
|
if (cmdbuf->c_error) {
|
||||||
|
DEBUG("sdcard: MMC_READ_BLOCK_%s failed with %d\n", cmdbuf->c_opcode == MMC_READ_BLOCK_MULTIPLE ? "MULTIPLE" : "SINGLE", cmdbuf->c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(cmdbuf->c_opcode == MMC_READ_BLOCK_MULTIPLE)
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_READ_BLOCK_MULTIPLE finished\n"));
|
||||||
|
else
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_READ_BLOCK_SINGLE finished\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_read(u32 blk_start, u32 blk_count, void *data)
|
||||||
|
{
|
||||||
|
struct sdmmc_command cmd;
|
||||||
|
|
||||||
|
// DEBUG("%s(%u, %u, %p)\n", __FUNCTION__, blk_start, blk_count, data);
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: READ: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.selected == 0) {
|
||||||
|
if (sdcard_select() < 0) {
|
||||||
|
DEBUG("sdcard: READ: cannot select card.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
|
||||||
|
if(blk_count > 1) {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_READ_BLOCK_MULTIPLE\n"));
|
||||||
|
cmd.c_opcode = MMC_READ_BLOCK_MULTIPLE;
|
||||||
|
} else {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_READ_BLOCK_SINGLE\n"));
|
||||||
|
cmd.c_opcode = MMC_READ_BLOCK_SINGLE;
|
||||||
|
}
|
||||||
|
if (card.sdhc_blockmode)
|
||||||
|
cmd.c_arg = blk_start;
|
||||||
|
else
|
||||||
|
cmd.c_arg = blk_start * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_data = data;
|
||||||
|
cmd.c_datalen = blk_count * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_blklen = SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_flags = SCF_RSP_R1 | SCF_CMD_READ;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_READ_BLOCK_%s failed with %d\n", blk_count > 1 ? "MULTIPLE" : "SINGLE", cmd.c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(blk_count > 1)
|
||||||
|
DPRINTF(2, ("sdcard: MMC_READ_BLOCK_MULTIPLE done\n"));
|
||||||
|
else
|
||||||
|
DPRINTF(2, ("sdcard: MMC_READ_BLOCK_SINGLE done\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LOADER
|
||||||
|
int sdcard_start_write(u32 blk_start, u32 blk_count, void *data, struct sdmmc_command* cmdbuf)
|
||||||
|
{
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: WRITE: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.selected == 0) {
|
||||||
|
if (sdcard_select() < 0) {
|
||||||
|
DEBUG("sdcard: WRITE: cannot select card.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(cmdbuf, 0, sizeof(struct sdmmc_command));
|
||||||
|
|
||||||
|
if(blk_count > 1) {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_WRITE_BLOCK_MULTIPLE\n"));
|
||||||
|
cmdbuf->c_opcode = MMC_WRITE_BLOCK_MULTIPLE;
|
||||||
|
} else {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_WRITE_BLOCK_SINGLE\n"));
|
||||||
|
cmdbuf->c_opcode = MMC_WRITE_BLOCK_SINGLE;
|
||||||
|
}
|
||||||
|
if (card.sdhc_blockmode)
|
||||||
|
cmdbuf->c_arg = blk_start;
|
||||||
|
else
|
||||||
|
cmdbuf->c_arg = blk_start * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmdbuf->c_data = data;
|
||||||
|
cmdbuf->c_datalen = blk_count * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmdbuf->c_blklen = SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmdbuf->c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_async_command(card.handle, cmdbuf);
|
||||||
|
|
||||||
|
if (cmdbuf->c_error) {
|
||||||
|
DEBUG("sdcard: MMC_WRITE_BLOCK_%s failed with %d\n", blk_count > 1 ? "MULTIPLE" : "SINGLE", cmdbuf->c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(blk_count > 1)
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_WRITE_BLOCK_MULTIPLE started\n"));
|
||||||
|
else
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_WRITE_BLOCK_SINGLE started\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_end_write(struct sdmmc_command* cmdbuf)
|
||||||
|
{
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: WRITE: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.selected == 0) {
|
||||||
|
if (sdcard_select() < 0) {
|
||||||
|
DEBUG("sdcard: WRITE: cannot select card.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhc_async_response(card.handle, cmdbuf);
|
||||||
|
|
||||||
|
if (cmdbuf->c_error) {
|
||||||
|
DEBUG("sdcard: MMC_WRITE_BLOCK_%s failed with %d\n", cmdbuf->c_opcode == MMC_WRITE_BLOCK_MULTIPLE ? "MULTIPLE" : "SINGLE", cmdbuf->c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(cmdbuf->c_opcode == MMC_WRITE_BLOCK_MULTIPLE)
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_WRITE_BLOCK_MULTIPLE finished\n"));
|
||||||
|
else
|
||||||
|
DPRINTF(2, ("sdcard: async MMC_WRITE_BLOCK_SINGLE finished\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_write(u32 blk_start, u32 blk_count, void *data)
|
||||||
|
{
|
||||||
|
struct sdmmc_command cmd;
|
||||||
|
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: WRITE: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.selected == 0) {
|
||||||
|
if (sdcard_select() < 0) {
|
||||||
|
DEBUG("sdcard: WRITE: cannot select card.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
|
||||||
|
if(blk_count > 1) {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_WRITE_BLOCK_MULTIPLE\n"));
|
||||||
|
cmd.c_opcode = MMC_WRITE_BLOCK_MULTIPLE;
|
||||||
|
} else {
|
||||||
|
DPRINTF(2, ("sdcard: MMC_WRITE_BLOCK_SINGLE\n"));
|
||||||
|
cmd.c_opcode = MMC_WRITE_BLOCK_SINGLE;
|
||||||
|
}
|
||||||
|
if (card.sdhc_blockmode)
|
||||||
|
cmd.c_arg = blk_start;
|
||||||
|
else
|
||||||
|
cmd.c_arg = blk_start * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_data = data;
|
||||||
|
cmd.c_datalen = blk_count * SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_blklen = SDMMC_DEFAULT_BLOCKLEN;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_WRITE_BLOCK_%s failed with %d\n", blk_count > 1 ? "MULTIPLE" : "SINGLE", cmd.c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(blk_count > 1)
|
||||||
|
DPRINTF(2, ("sdcard: MMC_WRITE_BLOCK_MULTIPLE done\n"));
|
||||||
|
else
|
||||||
|
DPRINTF(2, ("sdcard: MMC_WRITE_BLOCK_SINGLE done\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_wait_data(void)
|
||||||
|
{
|
||||||
|
struct sdmmc_command cmd;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
DPRINTF(2, ("sdcard: MMC_SEND_STATUS\n"));
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
|
cmd.c_opcode = MMC_SEND_STATUS;
|
||||||
|
cmd.c_arg = ((u32)card.rca)<<16;
|
||||||
|
cmd.c_flags = SCF_RSP_R1;
|
||||||
|
sdhc_exec_command(card.handle, &cmd);
|
||||||
|
|
||||||
|
if (cmd.c_error) {
|
||||||
|
DEBUG("sdcard: MMC_SEND_STATUS failed with %d\n", cmd.c_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sdcard_get_sectors(void)
|
||||||
|
{
|
||||||
|
if (card.inserted == 0) {
|
||||||
|
DEBUG("sdcard: READ: no card inserted.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card.new_card == 1) {
|
||||||
|
DEBUG("sdcard: new card inserted but not acknowledged yet.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sdhc_error(sdhci->reg_base, "num sectors = %u", sdhci->num_sectors);
|
||||||
|
|
||||||
|
return card.num_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdcard_irq(void)
|
||||||
|
{
|
||||||
|
sdhc_intr(&sdcard_host);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdcard_init(void)
|
||||||
|
{
|
||||||
|
struct sdhc_host_params params = {
|
||||||
|
.attach = &sdcard_attach,
|
||||||
|
.abort = &sdcard_abort,
|
||||||
|
.rb = RB_SD0,
|
||||||
|
.wb = WB_SD0,
|
||||||
|
};
|
||||||
|
|
||||||
|
clear32(LT_GPIO_INTMASK, GP_SDSLOT0_PWR);
|
||||||
|
set32(LT_GPIO_DIR, GP_SDSLOT0_PWR);
|
||||||
|
set32(LT_GPIO_ENABLE, GP_SDSLOT0_PWR);
|
||||||
|
clear32(LT_GPIO_OUT, GP_SDSLOT0_PWR);
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
#ifdef CAN_HAZ_IRQ
|
||||||
|
irq_enable(IRQ_SD0);
|
||||||
|
#endif
|
||||||
|
sdhc_host_found(&sdcard_host, ¶ms, 0, SD0_REG_BASE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdcard_exit(void)
|
||||||
|
{
|
||||||
|
#ifdef CAN_HAZ_IRQ
|
||||||
|
irq_disable(IRQ_SD0);
|
||||||
|
#endif
|
||||||
|
sdhc_shutdown(&sdcard_host);
|
||||||
|
#ifdef CAN_HAZ_IRQ
|
||||||
|
irq_disable(IRQ_SD0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
41
stage2/sdcard.h
Normal file
41
stage2/sdcard.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SDCARD_H__
|
||||||
|
#define __SDCARD_H__
|
||||||
|
|
||||||
|
#include "bsdtypes.h"
|
||||||
|
#include "sdmmc.h"
|
||||||
|
|
||||||
|
void sdcard_init(void);
|
||||||
|
void sdcard_exit(void);
|
||||||
|
void sdcard_irq(void);
|
||||||
|
|
||||||
|
void sdcard_attach(sdmmc_chipset_handle_t handle);
|
||||||
|
void sdcard_needs_discover(void);
|
||||||
|
int sdcard_wait_data(void);
|
||||||
|
|
||||||
|
int sdcard_select(void);
|
||||||
|
int sdcard_check_card(void);
|
||||||
|
int sdcard_ack_card(void);
|
||||||
|
int sdcard_get_sectors(void);
|
||||||
|
|
||||||
|
int sdcard_read(u32 blk_start, u32 blk_count, void *data);
|
||||||
|
int sdcard_write(u32 blk_start, u32 blk_count, void *data);
|
||||||
|
|
||||||
|
int sdcard_start_read(u32 blk_start, u32 blk_count, void *data, struct sdmmc_command* cmdbuf);
|
||||||
|
int sdcard_end_read(struct sdmmc_command* cmdbuf);
|
||||||
|
|
||||||
|
int sdcard_start_write(u32 blk_start, u32 blk_count, void *data, struct sdmmc_command* cmdbuf);
|
||||||
|
int sdcard_end_write(struct sdmmc_command* cmdbuf);
|
||||||
|
|
||||||
|
#endif
|
903
stage2/sdhc.c
Normal file
903
stage2/sdhc.c
Normal file
@ -0,0 +1,903 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||||
|
* Copyright (c) 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (c) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SD Host Controller driver based on the SD Host Controller Standard
|
||||||
|
* Simplified Specification Version 1.00 (www.sdcard.com).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bsdtypes.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "sdmmc.h"
|
||||||
|
#include "sdhc.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#ifdef CAN_HAZ_IRQ
|
||||||
|
#include "irq.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#define SDHC_DEBUG
|
||||||
|
|
||||||
|
#define SDHC_COMMAND_TIMEOUT 500
|
||||||
|
#define SDHC_TRANSFER_TIMEOUT 5000
|
||||||
|
|
||||||
|
#define sdhc_wait_intr(a,b,c) sdhc_wait_intr_debug(__func__, __LINE__, a, b, c)
|
||||||
|
|
||||||
|
static inline u32 bus_space_read_4(bus_space_handle_t ioh, u32 reg)
|
||||||
|
{
|
||||||
|
return read32(ioh + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 bus_space_read_2(bus_space_handle_t ioh, u32 reg)
|
||||||
|
{
|
||||||
|
if(reg & 3)
|
||||||
|
return (read32((ioh + reg) & ~3) & 0xffff0000) >> 16;
|
||||||
|
else
|
||||||
|
return (read32(ioh + reg) & 0xffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 bus_space_read_1(bus_space_handle_t ioh, u32 reg)
|
||||||
|
{
|
||||||
|
u32 mask;
|
||||||
|
u32 addr;
|
||||||
|
u8 shift;
|
||||||
|
|
||||||
|
shift = (reg & 3) * 8;
|
||||||
|
mask = (0xFF << shift);
|
||||||
|
addr = ioh + reg;
|
||||||
|
|
||||||
|
return (read32(addr & ~3) & mask) >> shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bus_space_write_4(bus_space_handle_t ioh, u32 r, u32 v)
|
||||||
|
{
|
||||||
|
write32(ioh + r, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bus_space_write_2(bus_space_handle_t ioh, u32 r, u16 v)
|
||||||
|
{
|
||||||
|
if(r & 3)
|
||||||
|
mask32((ioh + r) & ~3, 0xffff0000, v << 16);
|
||||||
|
else
|
||||||
|
mask32((ioh + r), 0xffff, ((u32)v));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bus_space_write_1(bus_space_handle_t ioh, u32 r, u8 v)
|
||||||
|
{
|
||||||
|
u32 mask;
|
||||||
|
u32 addr;
|
||||||
|
u8 shift;
|
||||||
|
|
||||||
|
shift = (r & 3) * 8;
|
||||||
|
mask = (0xFF << shift);
|
||||||
|
addr = ioh + r;
|
||||||
|
|
||||||
|
mask32(addr & ~3, mask, v << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* flag values */
|
||||||
|
#define SHF_USE_DMA 0x0001
|
||||||
|
|
||||||
|
#define HREAD1(hp, reg) \
|
||||||
|
(bus_space_read_1((hp)->ioh, (reg)))
|
||||||
|
#define HREAD2(hp, reg) \
|
||||||
|
(bus_space_read_2((hp)->ioh, (reg)))
|
||||||
|
#define HREAD4(hp, reg) \
|
||||||
|
(bus_space_read_4((hp)->ioh, (reg)))
|
||||||
|
#define HWRITE1(hp, reg, val) \
|
||||||
|
bus_space_write_1((hp)->ioh, (reg), (val))
|
||||||
|
#define HWRITE2(hp, reg, val) \
|
||||||
|
bus_space_write_2((hp)->ioh, (reg), (val))
|
||||||
|
#define HWRITE4(hp, reg, val) \
|
||||||
|
bus_space_write_4((hp)->ioh, (reg), (val))
|
||||||
|
#define HCLR1(hp, reg, bits) \
|
||||||
|
HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits))
|
||||||
|
#define HCLR2(hp, reg, bits) \
|
||||||
|
HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits))
|
||||||
|
#define HSET1(hp, reg, bits) \
|
||||||
|
HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits))
|
||||||
|
#define HSET2(hp, reg, bits) \
|
||||||
|
HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits))
|
||||||
|
|
||||||
|
int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *);
|
||||||
|
int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t);
|
||||||
|
int sdhc_soft_reset(struct sdhc_host *, int);
|
||||||
|
void sdhc_reset_intr_status(struct sdhc_host *hp);
|
||||||
|
int sdhc_wait_intr_debug(const char *func, int line, struct sdhc_host *, int, int);
|
||||||
|
void sdhc_transfer_data(struct sdhc_host *, struct sdmmc_command *);
|
||||||
|
void sdhc_read_data(struct sdhc_host *, u_char *, int);
|
||||||
|
void sdhc_write_data(struct sdhc_host *, u_char *, int);
|
||||||
|
#ifdef SDHC_DEBUG
|
||||||
|
int sdhcdebug = 3;
|
||||||
|
#define DPRINTF(n,s) do { if ((n) <= sdhcdebug) printf s; } while (0)
|
||||||
|
void sdhc_dump_regs(struct sdhc_host *);
|
||||||
|
#else
|
||||||
|
#define DPRINTF(n,s) do {} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by attachment driver. For each SD card slot there is one SD
|
||||||
|
* host controller standard register set. (1.3)
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sdhc_host_found(struct sdhc_host *hp, struct sdhc_host_params *pa, bus_space_tag_t iot, bus_space_handle_t ioh, int usedma)
|
||||||
|
{
|
||||||
|
u_int32_t caps;
|
||||||
|
int error = 1;
|
||||||
|
int max_clock;
|
||||||
|
|
||||||
|
#ifdef SDHC_DEBUG
|
||||||
|
u_int16_t version;
|
||||||
|
|
||||||
|
version = HREAD2(hp, SDHC_HOST_CTL_VERSION);
|
||||||
|
DEBUG("sdhc: SD Host Specification/Vendor Version ");
|
||||||
|
|
||||||
|
switch(SDHC_SPEC_VERSION(version)) {
|
||||||
|
case 0x00:
|
||||||
|
DEBUG("1.0/%u\n", SDHC_VENDOR_VERSION(version));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DEBUG(">1.0/%u\n", SDHC_VENDOR_VERSION(version));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(hp, 0, sizeof(struct sdhc_host));
|
||||||
|
|
||||||
|
/* Fill in the new host structure. */
|
||||||
|
hp->iot = iot;
|
||||||
|
hp->ioh = ioh;
|
||||||
|
hp->data_command = 0;
|
||||||
|
|
||||||
|
memcpy(&hp->pa, pa, sizeof(struct sdhc_host_params));
|
||||||
|
|
||||||
|
/* Store specification version. */
|
||||||
|
hp->version = HREAD2(hp, SDHC_HOST_CTL_VERSION);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the host controller and enable interrupts.
|
||||||
|
*/
|
||||||
|
(void)sdhc_host_reset(hp);
|
||||||
|
|
||||||
|
/* Determine host capabilities. */
|
||||||
|
caps = HREAD4(hp, SDHC_CAPABILITIES);
|
||||||
|
|
||||||
|
/* Use DMA if the host system and the controller support it. */
|
||||||
|
if (usedma && ISSET(caps, SDHC_DMA_SUPPORT))
|
||||||
|
SET(hp->flags, SHF_USE_DMA);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the base clock frequency. (2.2.24)
|
||||||
|
*/
|
||||||
|
if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
|
||||||
|
/* SDHC 3.0 supports 10-255 MHz. */
|
||||||
|
max_clock = 255000;
|
||||||
|
if (SDHC_BASE_FREQ_KHZ_V3(caps) != 0)
|
||||||
|
hp->clkbase = SDHC_BASE_FREQ_KHZ_V3(caps);
|
||||||
|
} else {
|
||||||
|
/* SDHC 1.0/2.0 supports only 10-63 MHz. */
|
||||||
|
max_clock = 63000;
|
||||||
|
if (SDHC_BASE_FREQ_KHZ(caps) != 0)
|
||||||
|
hp->clkbase = SDHC_BASE_FREQ_KHZ(caps);
|
||||||
|
}
|
||||||
|
if (hp->clkbase == 0) {
|
||||||
|
/* The attachment driver must tell us. */
|
||||||
|
DEBUG("sdhc: base clock frequency unknown\n");
|
||||||
|
goto err;
|
||||||
|
} else if (hp->clkbase < 10000 || hp->clkbase > max_clock) {
|
||||||
|
DEBUG("sdhc: base clock frequency out of range: %u MHz\n",
|
||||||
|
hp->clkbase / 1000);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("sdhc: SDHC %d.0, %d MHz base clock\n",
|
||||||
|
SDHC_SPEC_VERSION(hp->version) + 1, hp->clkbase / 1000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine SD bus voltage levels supported by the controller.
|
||||||
|
*/
|
||||||
|
if (ISSET(caps, SDHC_VOLTAGE_SUPP_1_8V))
|
||||||
|
SET(hp->ocr, MMC_OCR_1_9V_2_0V);
|
||||||
|
if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_0V))
|
||||||
|
SET(hp->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V);
|
||||||
|
if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_3V))
|
||||||
|
SET(hp->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach the generic SD/MMC bus driver. (The bus driver must
|
||||||
|
* not invoke any chipset functions before it is attached.)
|
||||||
|
*/
|
||||||
|
hp->pa.attach(hp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LOADER
|
||||||
|
/*
|
||||||
|
* Shutdown hook established by or called from attachment driver.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sdhc_shutdown(struct sdhc_host *hp)
|
||||||
|
{
|
||||||
|
/* XXX chip locks up if we don't disable it before reboot. */
|
||||||
|
(void)sdhc_host_reset(hp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the host controller. Called during initialization, when
|
||||||
|
* cards are removed, upon resume, and during error recovery.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sdhc_host_reset(struct sdhc_host *hp)
|
||||||
|
{
|
||||||
|
u_int16_t imask;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/* Disable all interrupts. */
|
||||||
|
HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the entire host controller and wait up to 100ms for
|
||||||
|
* the controller to clear the reset bit.
|
||||||
|
*/
|
||||||
|
if ((error = sdhc_soft_reset(hp, SDHC_RESET_ALL)) != 0) {
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set data timeout counter value to max for now. */
|
||||||
|
HWRITE1(hp, SDHC_TIMEOUT_CTL, SDHC_TIMEOUT_MAX);
|
||||||
|
|
||||||
|
/* Enable interrupts. */
|
||||||
|
imask =
|
||||||
|
#ifndef LOADER
|
||||||
|
SDHC_CARD_REMOVAL | SDHC_CARD_INSERTION |
|
||||||
|
#endif
|
||||||
|
SDHC_BUFFER_READ_READY | SDHC_BUFFER_WRITE_READY |
|
||||||
|
SDHC_DMA_INTERRUPT | SDHC_BLOCK_GAP_EVENT |
|
||||||
|
SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE;
|
||||||
|
|
||||||
|
HWRITE2(hp, SDHC_NINTR_STATUS_EN, imask);
|
||||||
|
HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK);
|
||||||
|
HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, imask);
|
||||||
|
HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, SDHC_EINTR_SIGNAL_MASK);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return non-zero if the card is currently inserted.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sdhc_card_detect(struct sdhc_host *hp)
|
||||||
|
{
|
||||||
|
return ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CARD_INSERTED) ?
|
||||||
|
1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set or change SD bus voltage and enable or disable SD bus power.
|
||||||
|
* Return zero on success.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sdhc_bus_power(struct sdhc_host *hp, u_int32_t ocr)
|
||||||
|
{
|
||||||
|
u_int8_t vdd;
|
||||||
|
DEBUG("sdhc_bus_power(0x%lx)\n", ocr);
|
||||||
|
|
||||||
|
/* Disable bus power before voltage change. */
|
||||||
|
HWRITE1(hp, SDHC_POWER_CTL, 0);
|
||||||
|
|
||||||
|
/* If power is disabled, reset the host and return now. */
|
||||||
|
if (ocr == 0) {
|
||||||
|
(void)sdhc_host_reset(hp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select the maximum voltage according to capabilities.
|
||||||
|
*/
|
||||||
|
ocr &= hp->ocr;
|
||||||
|
if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V))
|
||||||
|
vdd = SDHC_VOLTAGE_3_3V;
|
||||||
|
else if (ISSET(ocr, MMC_OCR_2_9V_3_0V|MMC_OCR_3_0V_3_1V))
|
||||||
|
vdd = SDHC_VOLTAGE_3_0V;
|
||||||
|
else if (ISSET(ocr, MMC_OCR_1_9V_2_0V))
|
||||||
|
vdd = SDHC_VOLTAGE_1_8V;
|
||||||
|
else {
|
||||||
|
/* Unsupported voltage level requested. */
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable bus power. Wait at least 1 ms (or 74 clocks) plus
|
||||||
|
* voltage ramp until power rises.
|
||||||
|
*/
|
||||||
|
HWRITE1(hp, SDHC_POWER_CTL, (vdd << SDHC_VOLTAGE_SHIFT) |
|
||||||
|
SDHC_BUS_POWER);
|
||||||
|
udelay(10000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The host system may not power the bus due to battery low,
|
||||||
|
* etc. In that case, the host controller should clear the
|
||||||
|
* bus power bit.
|
||||||
|
*/
|
||||||
|
if (!ISSET(HREAD1(hp, SDHC_POWER_CTL), SDHC_BUS_POWER)) {
|
||||||
|
DEBUG("Host controller failed to enable bus power\n");
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the smallest possible base clock frequency divisor value
|
||||||
|
* for the CLOCK_CTL register to produce `freq' (KHz).
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sdhc_clock_divisor(struct sdhc_host *hp, u_int freq)
|
||||||
|
{
|
||||||
|
int max_div = 256;
|
||||||
|
int div;
|
||||||
|
|
||||||
|
if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3)
|
||||||
|
max_div = 2046;
|
||||||
|
|
||||||
|
for (div = 1; div <= max_div; div *= 2)
|
||||||
|
if ((hp->clkbase / div) <= freq)
|
||||||
|
return (div / 2);
|
||||||
|
/* No divisor found. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set or change SDCLK frequency or disable the SD clock.
|
||||||
|
* Return zero on success.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sdhc_bus_clock(struct sdhc_host *hp, int freq, int timing)
|
||||||
|
{
|
||||||
|
int div;
|
||||||
|
int timo;
|
||||||
|
int sdclk;
|
||||||
|
|
||||||
|
DEBUG("%s(%d, %d)\n", __FUNCTION__, freq, timing);
|
||||||
|
#ifdef DIAGNOSTIC
|
||||||
|
/* Must not stop the clock if commands are in progress. */
|
||||||
|
if (ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CMD_INHIBIT_MASK) &&
|
||||||
|
sdhc_card_detect(hp))
|
||||||
|
DEBUG("sdhc_sdclk_frequency_select: command in progress\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Stop SD clock before changing the frequency. */
|
||||||
|
HWRITE2(hp, SDHC_CLOCK_CTL, 0);
|
||||||
|
if (freq == SDMMC_SDCLK_OFF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (timing == SDMMC_TIMING_LEGACY)
|
||||||
|
HCLR1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
|
||||||
|
else
|
||||||
|
HSET1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
|
||||||
|
|
||||||
|
/* Set the minimum base clock frequency divisor. */
|
||||||
|
if ((div = sdhc_clock_divisor(hp, freq)) < 0) {
|
||||||
|
/* Invalid base clock frequency or `freq' value. */
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3)
|
||||||
|
sdclk = SDHC_SDCLK_DIV_V3(div);
|
||||||
|
else
|
||||||
|
sdclk = SDHC_SDCLK_DIV(div);
|
||||||
|
HWRITE2(hp, SDHC_CLOCK_CTL, sdclk);
|
||||||
|
|
||||||
|
/* Start internal clock. Wait 10ms for stabilization. */
|
||||||
|
HSET2(hp, SDHC_CLOCK_CTL, SDHC_INTCLK_ENABLE);
|
||||||
|
for (timo = 1000; timo > 0; timo--) {
|
||||||
|
if (ISSET(HREAD2(hp, SDHC_CLOCK_CTL), SDHC_INTCLK_STABLE))
|
||||||
|
break;
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
if (timo == 0) {
|
||||||
|
DEBUG("sdhc: internal clock never stabilized\n");
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable SD clock. */
|
||||||
|
HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sdhc_bus_width(struct sdhc_host *hp, int width)
|
||||||
|
{
|
||||||
|
int reg;
|
||||||
|
|
||||||
|
DEBUG("%s(%d)\n", __FUNCTION__, width);
|
||||||
|
|
||||||
|
if (width != 1 && width != 4 && width != 8)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
reg = HREAD1(hp, SDHC_HOST_CTL);
|
||||||
|
reg &= ~(SDHC_4BIT_MODE | SDHC_8BIT_MODE);
|
||||||
|
|
||||||
|
if (width == 4) {
|
||||||
|
reg |= SDHC_4BIT_MODE;
|
||||||
|
} else if (width == 8) {
|
||||||
|
reg |= SDHC_8BIT_MODE;
|
||||||
|
}
|
||||||
|
HWRITE1(hp, SDHC_HOST_CTL, reg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sdhc_card_intr_mask(struct sdhc_host *hp, int enable)
|
||||||
|
{
|
||||||
|
if (enable) {
|
||||||
|
HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT);
|
||||||
|
HSET2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT);
|
||||||
|
} else {
|
||||||
|
HCLR2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT);
|
||||||
|
HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sdhc_card_intr_ack(struct sdhc_host *hp)
|
||||||
|
{
|
||||||
|
HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value)
|
||||||
|
{
|
||||||
|
u_int32_t state;
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
for (timeout = 500; timeout > 0; timeout--) {
|
||||||
|
if (((state = HREAD4(hp, SDHC_PRESENT_STATE)) & mask)
|
||||||
|
== value)
|
||||||
|
return 0;
|
||||||
|
udelay(10000);
|
||||||
|
}
|
||||||
|
DPRINTF(0,("sdhc: timeout waiting for %x (state=%d)\n", value, state));
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sdhc_exec_command(struct sdhc_host *hp, struct sdmmc_command *cmd)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (cmd->c_datalen > 0)
|
||||||
|
hp->data_command = 1;
|
||||||
|
|
||||||
|
if (cmd->c_timeout == 0) {
|
||||||
|
if (cmd->c_datalen > 0)
|
||||||
|
cmd->c_timeout = SDHC_TRANSFER_TIMEOUT;
|
||||||
|
else
|
||||||
|
cmd->c_timeout = SDHC_COMMAND_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
hp->intr_status = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the MMC command, or mark `cmd' as failed and return.
|
||||||
|
*/
|
||||||
|
error = sdhc_start_command(hp, cmd);
|
||||||
|
if (error != 0) {
|
||||||
|
cmd->c_error = error;
|
||||||
|
SET(cmd->c_flags, SCF_ITSDONE);
|
||||||
|
hp->data_command = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait until the command phase is done, or until the command
|
||||||
|
* is marked done for any other reason.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int status = sdhc_wait_intr(hp, SDHC_COMMAND_COMPLETE, cmd->c_timeout);
|
||||||
|
if (!ISSET(status, SDHC_COMMAND_COMPLETE)) {
|
||||||
|
cmd->c_error = ETIMEDOUT;
|
||||||
|
DEBUG("timeout dump: error_intr: 0x%x intr: 0x%x\n", hp->intr_error_status, hp->intr_status);
|
||||||
|
// sdhc_dump_regs(hp);
|
||||||
|
SET(cmd->c_flags, SCF_ITSDONE);
|
||||||
|
hp->data_command = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG("command_complete, continuing...\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The host controller removes bits [0:7] from the response
|
||||||
|
* data (CRC) and we pass the data up unchanged to the bus
|
||||||
|
* driver (without padding).
|
||||||
|
*/
|
||||||
|
if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_136)) {
|
||||||
|
u_char *p = (u_char *)cmd->c_resp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 15; i++)
|
||||||
|
*p++ = HREAD1(hp, SDHC_RESPONSE + i);
|
||||||
|
} else
|
||||||
|
cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the command has data to transfer in any direction,
|
||||||
|
* execute the transfer now.
|
||||||
|
*/
|
||||||
|
if (cmd->c_error == 0 && cmd->c_datalen > 0)
|
||||||
|
sdhc_transfer_data(hp, cmd);
|
||||||
|
|
||||||
|
DPRINTF(1,("sdhc: cmd %u done (flags=%#x error=%d prev state=%d)\n",
|
||||||
|
cmd->c_opcode, cmd->c_flags, cmd->c_error, (cmd->c_resp[0] >> 9) & 15));
|
||||||
|
SET(cmd->c_flags, SCF_ITSDONE);
|
||||||
|
hp->data_command = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd)
|
||||||
|
{
|
||||||
|
u_int16_t blksize = 0;
|
||||||
|
u_int16_t blkcount = 0;
|
||||||
|
u_int16_t mode;
|
||||||
|
u_int16_t command;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
DPRINTF(1,("sdhc: start cmd %u arg=%#x data=%p dlen=%d flags=%#x\n",
|
||||||
|
cmd->c_opcode, cmd->c_arg, cmd->c_data, cmd->c_datalen, cmd->c_flags));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The maximum block length for commands should be the minimum
|
||||||
|
* of the host buffer size and the card buffer size. (1.7.2)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Fragment the data into proper blocks. */
|
||||||
|
if (cmd->c_datalen > 0) {
|
||||||
|
blksize = MIN(cmd->c_datalen, cmd->c_blklen);
|
||||||
|
blkcount = cmd->c_datalen / blksize;
|
||||||
|
if (cmd->c_datalen % blksize > 0) {
|
||||||
|
/* XXX: Split this command. (1.7.4) */
|
||||||
|
DEBUG("sdhc: data not a multiple of %d bytes\n", blksize);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check limit imposed by 9-bit block count. (1.7.2) */
|
||||||
|
if (blkcount > SDHC_BLOCK_COUNT_MAX) {
|
||||||
|
DEBUG("sdhc: too much data\n");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare transfer mode register value. (2.2.5) */
|
||||||
|
mode = 0;
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ))
|
||||||
|
mode |= SDHC_READ_MODE;
|
||||||
|
if (blkcount > 0) {
|
||||||
|
mode |= SDHC_BLOCK_COUNT_ENABLE;
|
||||||
|
if (blkcount > 1) {
|
||||||
|
mode |= SDHC_MULTI_BLOCK_MODE;
|
||||||
|
/* XXX only for memory commands? */
|
||||||
|
mode |= SDHC_AUTO_CMD12_ENABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ISSET(hp->flags, SHF_USE_DMA))
|
||||||
|
mode |= SDHC_DMA_ENABLE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare command register value. (2.2.6)
|
||||||
|
*/
|
||||||
|
command = (cmd->c_opcode & SDHC_COMMAND_INDEX_MASK) <<
|
||||||
|
SDHC_COMMAND_INDEX_SHIFT;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_CRC))
|
||||||
|
command |= SDHC_CRC_CHECK_ENABLE;
|
||||||
|
if (ISSET(cmd->c_flags, SCF_RSP_IDX))
|
||||||
|
command |= SDHC_INDEX_CHECK_ENABLE;
|
||||||
|
if (cmd->c_data != NULL)
|
||||||
|
command |= SDHC_DATA_PRESENT_SELECT;
|
||||||
|
|
||||||
|
if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT))
|
||||||
|
command |= SDHC_NO_RESPONSE;
|
||||||
|
else if (ISSET(cmd->c_flags, SCF_RSP_136))
|
||||||
|
command |= SDHC_RESP_LEN_136;
|
||||||
|
else if (ISSET(cmd->c_flags, SCF_RSP_BSY))
|
||||||
|
command |= SDHC_RESP_LEN_48_CHK_BUSY;
|
||||||
|
else
|
||||||
|
command |= SDHC_RESP_LEN_48;
|
||||||
|
|
||||||
|
/* Wait until command and data inhibit bits are clear. (1.5) */
|
||||||
|
if ((error = sdhc_wait_state(hp, SDHC_CMD_INHIBIT_MASK, 0)) != 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (ISSET(hp->flags, SHF_USE_DMA) && cmd->c_datalen > 0) {
|
||||||
|
cmd->c_resid = blkcount;
|
||||||
|
cmd->c_buf = cmd->c_data;
|
||||||
|
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ) == 0) {
|
||||||
|
dc_flushrange(cmd->c_data, cmd->c_datalen);
|
||||||
|
ahb_flush_to(hp->pa.rb);
|
||||||
|
}
|
||||||
|
HWRITE4(hp, SDHC_DMA_ADDR, (u32)cmd->c_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(1,("sdhc: cmd=%#x mode=%#x blksize=%d blkcount=%d\n",
|
||||||
|
command, mode, blksize, blkcount));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a CPU data transfer. Writing to the high order byte
|
||||||
|
* of the SDHC_COMMAND register triggers the SD command. (1.5)
|
||||||
|
*/
|
||||||
|
// HWRITE2(hp, SDHC_TRANSFER_MODE, mode);
|
||||||
|
HWRITE2(hp, SDHC_BLOCK_SIZE, blksize | 7 << 12);
|
||||||
|
HWRITE2(hp, SDHC_BLOCK_COUNT, blkcount);
|
||||||
|
HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg);
|
||||||
|
// http://wiibrew.org/wiki/Reversed_Little_Endian
|
||||||
|
// HWRITE2(hp, SDHC_COMMAND, command);
|
||||||
|
HWRITE4(hp, SDHC_TRANSFER_MODE, ((u32)command << 16) | mode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sdhc_transfer_data(struct sdhc_host *hp, struct sdmmc_command *cmd)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
|
||||||
|
DPRINTF(1,("resp=%#x datalen=%d\n", MMC_R1(cmd->c_resp), cmd->c_datalen));
|
||||||
|
if (ISSET(hp->flags, SHF_USE_DMA)) {
|
||||||
|
for(;;) {
|
||||||
|
status = sdhc_wait_intr(hp, SDHC_TRANSFER_COMPLETE |
|
||||||
|
SDHC_DMA_INTERRUPT,
|
||||||
|
SDHC_TRANSFER_TIMEOUT);
|
||||||
|
if (!status) {
|
||||||
|
DEBUG("DMA timeout %08x\n", status);
|
||||||
|
error = ETIMEDOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ISSET(status, SDHC_TRANSFER_COMPLETE)) {
|
||||||
|
// DEBUG("got a TRANSFER_COMPLETE: %08x\n", status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
DEBUG("fail.\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SDHC_DEBUG
|
||||||
|
/* XXX I forgot why I wanted to know when this happens :-( */
|
||||||
|
if ((cmd->c_opcode == 52 || cmd->c_opcode == 53) &&
|
||||||
|
ISSET(MMC_R1(cmd->c_resp), 0xcb00))
|
||||||
|
DEBUG("sdhc: CMD52/53 error response flags %#x\n",
|
||||||
|
MMC_R1(cmd->c_resp) & 0xff00);
|
||||||
|
#endif
|
||||||
|
if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
|
||||||
|
ahb_flush_from(hp->pa.wb);
|
||||||
|
dc_invalidaterange(cmd->c_data, cmd->c_datalen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != 0)
|
||||||
|
cmd->c_error = error;
|
||||||
|
SET(cmd->c_flags, SCF_ITSDONE);
|
||||||
|
|
||||||
|
DPRINTF(1,("sdhc: data transfer done (error=%d)\n", cmd->c_error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare for another command. */
|
||||||
|
int
|
||||||
|
sdhc_soft_reset(struct sdhc_host *hp, int mask)
|
||||||
|
{
|
||||||
|
int timo;
|
||||||
|
|
||||||
|
DPRINTF(1,("sdhc: software reset reg=%#x\n", mask));
|
||||||
|
|
||||||
|
HWRITE1(hp, SDHC_SOFTWARE_RESET, mask);
|
||||||
|
for (timo = 10; timo > 0; timo--) {
|
||||||
|
if (!ISSET(HREAD1(hp, SDHC_SOFTWARE_RESET), mask))
|
||||||
|
break;
|
||||||
|
udelay(10000);
|
||||||
|
HWRITE1(hp, SDHC_SOFTWARE_RESET, 0);
|
||||||
|
}
|
||||||
|
if (timo == 0) {
|
||||||
|
DPRINTF(1,("sdhc: timeout reg=%#x\n", HREAD1(hp, SDHC_SOFTWARE_RESET)));
|
||||||
|
HWRITE1(hp, SDHC_SOFTWARE_RESET, 0);
|
||||||
|
return (ETIMEDOUT);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sdhc_wait_intr_debug(const char *funcname, int line, struct sdhc_host *hp, int mask, int timo)
|
||||||
|
{
|
||||||
|
(void) funcname;
|
||||||
|
(void) line;
|
||||||
|
|
||||||
|
int status;
|
||||||
|
|
||||||
|
mask |= SDHC_ERROR_INTERRUPT;
|
||||||
|
mask |= SDHC_ERROR_TIMEOUT;
|
||||||
|
|
||||||
|
status = hp->intr_status & mask;
|
||||||
|
|
||||||
|
for (; timo > 0; timo--) {
|
||||||
|
#ifdef CAN_HAZ_IRQ
|
||||||
|
if((get_cpsr() & 0b11111) == 0b10010)
|
||||||
|
#endif
|
||||||
|
sdhc_intr(hp); // seems backwards but ok
|
||||||
|
|
||||||
|
if (hp->intr_status != 0) {
|
||||||
|
status = hp->intr_status & mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
udelay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timo == 0) {
|
||||||
|
status |= SDHC_ERROR_TIMEOUT;
|
||||||
|
}
|
||||||
|
hp->intr_status &= ~status;
|
||||||
|
|
||||||
|
DPRINTF(2,("sdhc: funcname=%s, line=%d, timo=%d status=%#x intr status=%#x error %#x\n",
|
||||||
|
funcname, line, timo, status, hp->intr_status, hp->intr_error_status));
|
||||||
|
|
||||||
|
/* Command timeout has higher priority than command complete. */
|
||||||
|
if (ISSET(status, SDHC_ERROR_INTERRUPT)) {
|
||||||
|
DEBUG("resetting due to error interrupt\n");
|
||||||
|
// sdhc_dump_regs(hp);
|
||||||
|
|
||||||
|
hp->intr_error_status = 0;
|
||||||
|
(void)sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD);
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command timeout has higher priority than command complete. */
|
||||||
|
if (ISSET(status, SDHC_ERROR_TIMEOUT)) {
|
||||||
|
DEBUG("resetting due to timeout\n");
|
||||||
|
// sdhc_dump_regs(hp);
|
||||||
|
|
||||||
|
hp->intr_error_status = 0;
|
||||||
|
(void)sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD);
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Established by attachment driver at interrupt priority IPL_SDMMC.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sdhc_intr(struct sdhc_host *hp)
|
||||||
|
{
|
||||||
|
u_int16_t status;
|
||||||
|
u_int16_t error;
|
||||||
|
u_int16_t signal;
|
||||||
|
|
||||||
|
DPRINTF(1,("sdhc_intr():\n"));
|
||||||
|
// sdhc_dump_regs(hp);
|
||||||
|
|
||||||
|
/* Find out which interrupts are pending. */
|
||||||
|
status = HREAD2(hp, SDHC_NINTR_STATUS);
|
||||||
|
if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) {
|
||||||
|
DPRINTF(1, ("unknown interrupt\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = HREAD2(hp, SDHC_EINTR_STATUS);
|
||||||
|
signal = HREAD2(hp, SDHC_EINTR_SIGNAL_EN);
|
||||||
|
|
||||||
|
/* Acknowledge the interrupts we are about to handle. */
|
||||||
|
HWRITE2(hp, SDHC_NINTR_STATUS, status);
|
||||||
|
DPRINTF(2,("sdhc: interrupt status=%d\n", status));
|
||||||
|
|
||||||
|
/* Service error interrupts. */
|
||||||
|
if (ISSET(status, SDHC_ERROR_INTERRUPT)) {
|
||||||
|
/* Acknowledge error interrupts. */
|
||||||
|
HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, 0);
|
||||||
|
(void)sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD);
|
||||||
|
if (hp->data_command == 1) {
|
||||||
|
hp->data_command = 0;
|
||||||
|
hp->pa.abort();
|
||||||
|
}
|
||||||
|
HWRITE2(hp, SDHC_EINTR_STATUS, error);
|
||||||
|
HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, signal);
|
||||||
|
|
||||||
|
DPRINTF(2,("sdhc: error interrupt, status=0x%x, signal=0x%x\n", error, signal));
|
||||||
|
|
||||||
|
if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR|
|
||||||
|
SDHC_DATA_TIMEOUT_ERROR)) {
|
||||||
|
hp->intr_error_status |= error;
|
||||||
|
hp->intr_status |= status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up the blocking process to service command
|
||||||
|
* related interrupt(s).
|
||||||
|
*/
|
||||||
|
if (ISSET(status, SDHC_BUFFER_READ_READY|
|
||||||
|
SDHC_BUFFER_WRITE_READY|SDHC_COMMAND_COMPLETE|
|
||||||
|
SDHC_TRANSFER_COMPLETE)) {
|
||||||
|
hp->intr_status |= status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ISSET(status, SDHC_DMA_INTERRUPT)) {
|
||||||
|
DPRINTF(2,("sdhc: dma left:%#x\n", HREAD2(hp, SDHC_BLOCK_COUNT)));
|
||||||
|
// this works because our virtual memory
|
||||||
|
// addresses are equal to the physical memory
|
||||||
|
// addresses and because we require the target
|
||||||
|
// buffer to be contiguous
|
||||||
|
HWRITE4(hp, SDHC_DMA_ADDR, HREAD4(hp, SDHC_DMA_ADDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Service SD card interrupts. */
|
||||||
|
if (ISSET(status, SDHC_CARD_INTERRUPT)) {
|
||||||
|
DPRINTF(0,("sdhc: card interrupt\n"));
|
||||||
|
HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up the sdmmc event thread to scan for cards.
|
||||||
|
*/
|
||||||
|
if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION))
|
||||||
|
hp->pa.attach(hp);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SDHC_DEBUG
|
||||||
|
void
|
||||||
|
sdhc_dump_regs(struct sdhc_host *hp)
|
||||||
|
{
|
||||||
|
DEBUG("0x%02x PRESENT_STATE: %x\n", SDHC_PRESENT_STATE,
|
||||||
|
HREAD4(hp, SDHC_PRESENT_STATE));
|
||||||
|
DEBUG("0x%02x POWER_CTL: %x\n", SDHC_POWER_CTL,
|
||||||
|
HREAD1(hp, SDHC_POWER_CTL));
|
||||||
|
DEBUG("0x%02x NINTR_STATUS: %x\n", SDHC_NINTR_STATUS,
|
||||||
|
HREAD2(hp, SDHC_NINTR_STATUS));
|
||||||
|
DEBUG("0x%02x EINTR_STATUS: %x\n", SDHC_EINTR_STATUS,
|
||||||
|
HREAD2(hp, SDHC_EINTR_STATUS));
|
||||||
|
DEBUG("0x%02x NINTR_STATUS_EN: %x\n", SDHC_NINTR_STATUS_EN,
|
||||||
|
HREAD2(hp, SDHC_NINTR_STATUS_EN));
|
||||||
|
DEBUG("0x%02x EINTR_STATUS_EN: %x\n", SDHC_EINTR_STATUS_EN,
|
||||||
|
HREAD2(hp, SDHC_EINTR_STATUS_EN));
|
||||||
|
DEBUG("0x%02x NINTR_SIGNAL_EN: %x\n", SDHC_NINTR_SIGNAL_EN,
|
||||||
|
HREAD2(hp, SDHC_NINTR_SIGNAL_EN));
|
||||||
|
DEBUG("0x%02x EINTR_SIGNAL_EN: %x\n", SDHC_EINTR_SIGNAL_EN,
|
||||||
|
HREAD2(hp, SDHC_EINTR_SIGNAL_EN));
|
||||||
|
DEBUG("0x%02x CAPABILITIES: %x\n", SDHC_CAPABILITIES,
|
||||||
|
HREAD4(hp, SDHC_CAPABILITIES));
|
||||||
|
DEBUG("0x%02x MAX_CAPABILITIES: %x\n", SDHC_MAX_CAPABILITIES,
|
||||||
|
HREAD4(hp, SDHC_MAX_CAPABILITIES));
|
||||||
|
}
|
||||||
|
#endif
|
233
stage2/sdhc.h
Normal file
233
stage2/sdhc.h
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/* $OpenBSD: sdhcvar.h,v 1.3 2007/09/06 08:01:01 jsg Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||||
|
* Copyright (c) 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
* Copyright (c) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SDHCVAR_H_
|
||||||
|
#define _SDHCVAR_H_
|
||||||
|
|
||||||
|
#include "bsdtypes.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "sdmmc.h"
|
||||||
|
|
||||||
|
struct sdhc_host_params {
|
||||||
|
void (*attach)();
|
||||||
|
void (*abort)();
|
||||||
|
enum rb_client rb;
|
||||||
|
enum wb_client wb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhc_host {
|
||||||
|
bus_space_tag_t iot; /* host register set tag */
|
||||||
|
bus_space_handle_t ioh; /* host register set handle */
|
||||||
|
u_int16_t version; /* specification version */
|
||||||
|
u_int clkbase; /* base clock frequency in KHz */
|
||||||
|
int flags; /* flags for this host */
|
||||||
|
u_int32_t ocr; /* OCR value from capabilities */
|
||||||
|
u_int8_t regs[14]; /* host controller state */
|
||||||
|
volatile u_int16_t intr_status; /* soft interrupt status */
|
||||||
|
volatile u_int16_t intr_error_status; /* soft error status */
|
||||||
|
int data_command;
|
||||||
|
|
||||||
|
struct sdhc_host_params pa;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Host controller functions called by the attachment driver. */
|
||||||
|
int sdhc_host_found(struct sdhc_host *, struct sdhc_host_params *, bus_space_tag_t, bus_space_handle_t, int);
|
||||||
|
void sdhc_power(int, void *);
|
||||||
|
void sdhc_shutdown(struct sdhc_host *);
|
||||||
|
int sdhc_intr(struct sdhc_host *);
|
||||||
|
|
||||||
|
/* Host standard register set */
|
||||||
|
#define SDHC_DMA_ADDR 0x00
|
||||||
|
#define SDHC_BLOCK_SIZE 0x04
|
||||||
|
#define SDHC_BLOCK_COUNT 0x06
|
||||||
|
#define SDHC_BLOCK_COUNT_MAX 512
|
||||||
|
#define SDHC_ARGUMENT 0x08
|
||||||
|
#define SDHC_TRANSFER_MODE 0x0c
|
||||||
|
#define SDHC_MULTI_BLOCK_MODE (1<<5)
|
||||||
|
#define SDHC_READ_MODE (1<<4)
|
||||||
|
#define SDHC_AUTO_CMD12_ENABLE (1<<2)
|
||||||
|
#define SDHC_BLOCK_COUNT_ENABLE (1<<1)
|
||||||
|
#define SDHC_DMA_ENABLE (1<<0)
|
||||||
|
#define SDHC_COMMAND 0x0e
|
||||||
|
/* 14-15 reserved */
|
||||||
|
#define SDHC_COMMAND_INDEX_SHIFT 8
|
||||||
|
#define SDHC_COMMAND_INDEX_MASK 0x3f
|
||||||
|
#define SDHC_COMMAND_TYPE_ABORT (3<<6)
|
||||||
|
#define SDHC_COMMAND_TYPE_RESUME (2<<6)
|
||||||
|
#define SDHC_COMMAND_TYPE_SUSPEND (1<<6)
|
||||||
|
#define SDHC_COMMAND_TYPE_NORMAL (0<<6)
|
||||||
|
#define SDHC_DATA_PRESENT_SELECT (1<<5)
|
||||||
|
#define SDHC_INDEX_CHECK_ENABLE (1<<4)
|
||||||
|
#define SDHC_CRC_CHECK_ENABLE (1<<3)
|
||||||
|
/* 2 reserved */
|
||||||
|
#define SDHC_RESP_LEN_48_CHK_BUSY (3<<0)
|
||||||
|
#define SDHC_RESP_LEN_48 (2<<0)
|
||||||
|
#define SDHC_RESP_LEN_136 (1<<0)
|
||||||
|
#define SDHC_NO_RESPONSE (0<<0)
|
||||||
|
#define SDHC_RESPONSE 0x10 /* - 0x1f */
|
||||||
|
#define SDHC_DATA 0x20
|
||||||
|
#define SDHC_PRESENT_STATE 0x24
|
||||||
|
/* 25-31 reserved */
|
||||||
|
#define SDHC_CMD_LINE_SIGNAL_LEVEL (1<<24)
|
||||||
|
#define SDHC_DAT3_LINE_LEVEL (1<<23)
|
||||||
|
#define SDHC_DAT2_LINE_LEVEL (1<<22)
|
||||||
|
#define SDHC_DAT1_LINE_LEVEL (1<<21)
|
||||||
|
#define SDHC_DAT0_LINE_LEVEL (1<<20)
|
||||||
|
#define SDHC_WRITE_PROTECT_SWITCH (1<<19)
|
||||||
|
#define SDHC_CARD_DETECT_PIN_LEVEL (1<<18)
|
||||||
|
#define SDHC_CARD_STATE_STABLE (1<<17)
|
||||||
|
#define SDHC_CARD_INSERTED (1<<16)
|
||||||
|
/* 12-15 reserved */
|
||||||
|
#define SDHC_BUFFER_READ_ENABLE (1<<11)
|
||||||
|
#define SDHC_BUFFER_WRITE_ENABLE (1<<10)
|
||||||
|
#define SDHC_READ_TRANSFER_ACTIVE (1<<9)
|
||||||
|
#define SDHC_WRITE_TRANSFER_ACTIVE (1<<8)
|
||||||
|
/* 3-7 reserved */
|
||||||
|
#define SDHC_DAT_ACTIVE (1<<2)
|
||||||
|
#define SDHC_CMD_INHIBIT_DAT (1<<1)
|
||||||
|
#define SDHC_CMD_INHIBIT_CMD (1<<0)
|
||||||
|
#define SDHC_CMD_INHIBIT_MASK 0x0003
|
||||||
|
#define SDHC_HOST_CTL 0x28
|
||||||
|
#define SDHC_8BIT_MODE (1<<5)
|
||||||
|
#define SDHC_HIGH_SPEED (1<<2)
|
||||||
|
#define SDHC_4BIT_MODE (1<<1)
|
||||||
|
#define SDHC_LED_ON (1<<0)
|
||||||
|
#define SDHC_POWER_CTL 0x29
|
||||||
|
#define SDHC_VOLTAGE_SHIFT 1
|
||||||
|
#define SDHC_VOLTAGE_MASK 0x07
|
||||||
|
#define SDHC_VOLTAGE_3_3V 0x07
|
||||||
|
#define SDHC_VOLTAGE_3_0V 0x06
|
||||||
|
#define SDHC_VOLTAGE_1_8V 0x05
|
||||||
|
#define SDHC_BUS_POWER (1<<0)
|
||||||
|
#define SDHC_BLOCK_GAP_CTL 0x2a
|
||||||
|
#define SDHC_WAKEUP_CTL 0x2b
|
||||||
|
#define SDHC_CLOCK_CTL 0x2c
|
||||||
|
#define SDHC_SDCLK_DIV_SHIFT 8
|
||||||
|
#define SDHC_SDCLK_DIV_MASK 0xff
|
||||||
|
#define SDHC_SDCLK_DIV_RSHIFT_V3 2
|
||||||
|
#define SDHC_SDCLK_DIV_MASK_V3 0x300
|
||||||
|
#define SDHC_SDCLK_ENABLE (1<<2)
|
||||||
|
#define SDHC_INTCLK_STABLE (1<<1)
|
||||||
|
#define SDHC_INTCLK_ENABLE (1<<0)
|
||||||
|
#define SDHC_TIMEOUT_CTL 0x2e
|
||||||
|
#define SDHC_TIMEOUT_MAX 0x0e
|
||||||
|
#define SDHC_SOFTWARE_RESET 0x2f
|
||||||
|
#define SDHC_RESET_MASK 0x5
|
||||||
|
#define SDHC_RESET_DAT (1<<2)
|
||||||
|
#define SDHC_RESET_CMD (1<<1)
|
||||||
|
#define SDHC_RESET_ALL (1<<0)
|
||||||
|
#define SDHC_NINTR_STATUS 0x30
|
||||||
|
#define SDHC_ERROR_INTERRUPT (1<<15)
|
||||||
|
#define SDHC_ERROR_TIMEOUT (1<<14)
|
||||||
|
#define SDHC_CARD_INTERRUPT (1<<8)
|
||||||
|
#define SDHC_CARD_REMOVAL (1<<7)
|
||||||
|
#define SDHC_CARD_INSERTION (1<<6)
|
||||||
|
#define SDHC_BUFFER_READ_READY (1<<5)
|
||||||
|
#define SDHC_BUFFER_WRITE_READY (1<<4)
|
||||||
|
#define SDHC_DMA_INTERRUPT (1<<3)
|
||||||
|
#define SDHC_BLOCK_GAP_EVENT (1<<2)
|
||||||
|
#define SDHC_TRANSFER_COMPLETE (1<<1)
|
||||||
|
#define SDHC_COMMAND_COMPLETE (1<<0)
|
||||||
|
#define SDHC_NINTR_STATUS_MASK 0x81ff
|
||||||
|
#define SDHC_EINTR_STATUS 0x32
|
||||||
|
#define SDHC_ADMA_ERROR (1<<9)
|
||||||
|
#define SDHC_AUTO_CMD12_ERROR (1<<8)
|
||||||
|
#define SDHC_CURRENT_LIMIT_ERROR (1<<7)
|
||||||
|
#define SDHC_DATA_END_BIT_ERROR (1<<6)
|
||||||
|
#define SDHC_DATA_CRC_ERROR (1<<5)
|
||||||
|
#define SDHC_DATA_TIMEOUT_ERROR (1<<4)
|
||||||
|
#define SDHC_DATA_ERROR 0x70
|
||||||
|
#define SDHC_CMD_INDEX_ERROR (1<<3)
|
||||||
|
#define SDHC_CMD_END_BIT_ERROR (1<<2)
|
||||||
|
#define SDHC_CMD_CRC_ERROR (1<<1)
|
||||||
|
#define SDHC_CMD_TIMEOUT_ERROR (1<<0)
|
||||||
|
#define SDHC_CMD_ERROR 0x0f
|
||||||
|
#define SDHC_EINTR_STATUS_MASK 0x03ff /* excluding vendor signals */
|
||||||
|
#define SDHC_NINTR_STATUS_EN 0x34
|
||||||
|
#define SDHC_EINTR_STATUS_EN 0x36
|
||||||
|
#define SDHC_NINTR_SIGNAL_EN 0x38
|
||||||
|
#define SDHC_NINTR_SIGNAL_MASK 0x01ff
|
||||||
|
#define SDHC_EINTR_SIGNAL_EN 0x3a
|
||||||
|
#define SDHC_EINTR_SIGNAL_MASK 0x03ff /* excluding vendor signals */
|
||||||
|
#define SDHC_CMD12_ERROR_STATUS 0x3c
|
||||||
|
#define SDHC_CAPABILITIES 0x40
|
||||||
|
#define SDHC_VOLTAGE_SUPP_1_8V (1<<26)
|
||||||
|
#define SDHC_VOLTAGE_SUPP_3_0V (1<<25)
|
||||||
|
#define SDHC_VOLTAGE_SUPP_3_3V (1<<24)
|
||||||
|
#define SDHC_DMA_SUPPORT (1<<22)
|
||||||
|
#define SDHC_HIGH_SPEED_SUPP (1<<21)
|
||||||
|
#define SDHC_BASE_FREQ_SHIFT 8
|
||||||
|
#define SDHC_BASE_FREQ_MASK 0x3f
|
||||||
|
#define SDHC_BASE_FREQ_MASK_V3 0xff
|
||||||
|
#define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */
|
||||||
|
#define SDHC_TIMEOUT_FREQ_SHIFT 0
|
||||||
|
#define SDHC_TIMEOUT_FREQ_MASK 0x1f
|
||||||
|
#define SDHC_MAX_CAPABILITIES 0x48
|
||||||
|
#define SDHC_SLOT_INTR_STATUS 0xfc
|
||||||
|
#define SDHC_HOST_CTL_VERSION 0xfe
|
||||||
|
#define SDHC_SPEC_VERS_SHIFT 0
|
||||||
|
#define SDHC_SPEC_VERS_MASK 0xff
|
||||||
|
#define SDHC_VENDOR_VERS_SHIFT 8
|
||||||
|
#define SDHC_VENDOR_VERS_MASK 0xff
|
||||||
|
#define SDHC_SPEC_V1 0
|
||||||
|
#define SDHC_SPEC_V2 1
|
||||||
|
#define SDHC_SPEC_V3 2
|
||||||
|
|
||||||
|
/* SDHC_CLOCK_CTL encoding */
|
||||||
|
#define SDHC_SDCLK_DIV(div) \
|
||||||
|
(((div) & SDHC_SDCLK_DIV_MASK) << SDHC_SDCLK_DIV_SHIFT)
|
||||||
|
#define SDHC_SDCLK_DIV_V3(div) \
|
||||||
|
(SDHC_SDCLK_DIV(div) | \
|
||||||
|
(((div) & SDHC_SDCLK_DIV_MASK_V3) >> SDHC_SDCLK_DIV_RSHIFT_V3))
|
||||||
|
|
||||||
|
/* SDHC_CAPABILITIES decoding */
|
||||||
|
#define SDHC_BASE_FREQ_KHZ(cap) \
|
||||||
|
((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK) * 1000)
|
||||||
|
#define SDHC_BASE_FREQ_KHZ_V3(cap) \
|
||||||
|
((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK_V3) * 1000)
|
||||||
|
#define SDHC_TIMEOUT_FREQ(cap) \
|
||||||
|
(((cap) >> SDHC_TIMEOUT_FREQ_SHIFT) & SDHC_TIMEOUT_FREQ_MASK)
|
||||||
|
#define SDHC_TIMEOUT_FREQ_KHZ(cap) \
|
||||||
|
(((cap) & SDHC_TIMEOUT_FREQ_UNIT) ? \
|
||||||
|
SDHC_TIMEOUT_FREQ(cap) * 1000: \
|
||||||
|
SDHC_TIMEOUT_FREQ(cap))
|
||||||
|
|
||||||
|
/* SDHC_HOST_CTL_VERSION decoding */
|
||||||
|
#define SDHC_SPEC_VERSION(hcv) \
|
||||||
|
(((hcv) >> SDHC_SPEC_VERS_SHIFT) & SDHC_SPEC_VERS_MASK)
|
||||||
|
#define SDHC_VENDOR_VERSION(hcv) \
|
||||||
|
(((hcv) >> SDHC_VENDOR_VERS_SHIFT) & SDHC_VENDOR_VERS_MASK)
|
||||||
|
|
||||||
|
struct sdmmc_command;
|
||||||
|
|
||||||
|
int sdhc_host_reset(struct sdhc_host *hp);
|
||||||
|
int sdhc_card_detect(struct sdhc_host *hp);
|
||||||
|
int sdhc_bus_power(struct sdhc_host *hp, u_int32_t);
|
||||||
|
int sdhc_bus_clock(struct sdhc_host *hp, int, int);
|
||||||
|
int sdhc_bus_width(struct sdhc_host *hp, int);
|
||||||
|
void sdhc_card_intr_mask(struct sdhc_host *hp, int);
|
||||||
|
void sdhc_card_intr_ack(struct sdhc_host *hp);
|
||||||
|
|
||||||
|
void sdhc_exec_command(struct sdhc_host *hp, struct sdmmc_command *);
|
||||||
|
|
||||||
|
void sdhc_async_command(struct sdhc_host *hp, struct sdmmc_command *);
|
||||||
|
void sdhc_async_response(struct sdhc_host *hp, struct sdmmc_command *);
|
||||||
|
|
||||||
|
#endif
|
372
stage2/sdmmc.h
Normal file
372
stage2/sdmmc.h
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SDMMC_H__
|
||||||
|
#define __SDMMC_H__
|
||||||
|
|
||||||
|
#include "bsdtypes.h"
|
||||||
|
|
||||||
|
struct sdmmc_command;
|
||||||
|
|
||||||
|
typedef struct sdhc_host * sdmmc_chipset_handle_t;
|
||||||
|
|
||||||
|
/* clock frequencies for sdmmc_chip_bus_clock() */
|
||||||
|
#define SDMMC_SDCLK_OFF 0
|
||||||
|
#define SDMMC_SDCLK_400KHZ 400
|
||||||
|
#define SDMMC_SDCLK_25MHZ 25000
|
||||||
|
|
||||||
|
#define SDMMC_TIMING_LEGACY 0
|
||||||
|
#define SDMMC_TIMING_HIGHSPEED 1
|
||||||
|
|
||||||
|
struct sdmmc_csd {
|
||||||
|
int csdver; /* CSD structure format */
|
||||||
|
int mmcver; /* MMC version (for CID format) */
|
||||||
|
int capacity; /* total number of sectors */
|
||||||
|
int sector_size; /* sector size in bytes */
|
||||||
|
int read_bl_len; /* block length for reads */
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdmmc_cid {
|
||||||
|
int mid; /* manufacturer identification number */
|
||||||
|
int oid; /* OEM/product identification number */
|
||||||
|
char pnm[8]; /* product name (MMC v1 has the longest) */
|
||||||
|
int rev; /* product revision */
|
||||||
|
int psn; /* product serial number */
|
||||||
|
int mdt; /* manufacturing date */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u_int32_t sdmmc_response[4];
|
||||||
|
|
||||||
|
struct sdmmc_softc;
|
||||||
|
|
||||||
|
struct sdmmc_task {
|
||||||
|
void (*func)(void *arg);
|
||||||
|
void *arg;
|
||||||
|
int onqueue;
|
||||||
|
struct sdmmc_softc *sc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define sdmmc_init_task(xtask, xfunc, xarg) do { \
|
||||||
|
(xtask)->func = (xfunc); \
|
||||||
|
(xtask)->arg = (xarg); \
|
||||||
|
(xtask)->onqueue = 0; \
|
||||||
|
(xtask)->sc = NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define sdmmc_task_pending(xtask) ((xtask)->onqueue)
|
||||||
|
|
||||||
|
struct sdmmc_command {
|
||||||
|
// struct sdmmc_task c_task; /* task queue entry */
|
||||||
|
u_int16_t c_opcode; /* SD or MMC command index */
|
||||||
|
u_int32_t c_arg; /* SD/MMC command argument */
|
||||||
|
sdmmc_response c_resp; /* response buffer */
|
||||||
|
void *c_data; /* buffer to send or read into */
|
||||||
|
int c_datalen; /* length of data buffer */
|
||||||
|
int c_blklen; /* block length */
|
||||||
|
int c_flags; /* see below */
|
||||||
|
#define SCF_ITSDONE 0x0001 /* command is complete */
|
||||||
|
#define SCF_CMD(flags) ((flags) & 0x00f0)
|
||||||
|
#define SCF_CMD_AC 0x0000
|
||||||
|
#define SCF_CMD_ADTC 0x0010
|
||||||
|
#define SCF_CMD_BC 0x0020
|
||||||
|
#define SCF_CMD_BCR 0x0030
|
||||||
|
#define SCF_CMD_READ 0x0040 /* read command (data expected) */
|
||||||
|
#define SCF_RSP_BSY 0x0100
|
||||||
|
#define SCF_RSP_136 0x0200
|
||||||
|
#define SCF_RSP_CRC 0x0400
|
||||||
|
#define SCF_RSP_IDX 0x0800
|
||||||
|
#define SCF_RSP_PRESENT 0x1000
|
||||||
|
/* response types */
|
||||||
|
#define SCF_RSP_R0 0 /* none */
|
||||||
|
#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
|
#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||||
|
#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
|
||||||
|
#define SCF_RSP_R3 (SCF_RSP_PRESENT)
|
||||||
|
#define SCF_RSP_R4 (SCF_RSP_PRESENT)
|
||||||
|
#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
|
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
|
||||||
|
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
|
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
|
||||||
|
int c_error; /* errno value on completion */
|
||||||
|
|
||||||
|
int c_timeout;
|
||||||
|
|
||||||
|
/* Host controller owned fields for data xfer in progress */
|
||||||
|
int c_resid; /* remaining I/O */
|
||||||
|
u_char *c_buf; /* remaining data */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decoded PC Card 16 based Card Information Structure (CIS),
|
||||||
|
* per card (function 0) and per function (1 and greater).
|
||||||
|
*/
|
||||||
|
struct sdmmc_cis {
|
||||||
|
u_int16_t manufacturer;
|
||||||
|
#define SDMMC_VENDOR_INVALID 0xffff
|
||||||
|
u_int16_t product;
|
||||||
|
#define SDMMC_PRODUCT_INVALID 0xffff
|
||||||
|
u_int8_t function;
|
||||||
|
#define SDMMC_FUNCTION_INVALID 0xff
|
||||||
|
u_char cis1_major;
|
||||||
|
u_char cis1_minor;
|
||||||
|
char cis1_info_buf[256];
|
||||||
|
char *cis1_info[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure describing either an SD card I/O function or a SD/MMC
|
||||||
|
* memory card from a "stack of cards" that responded to CMD2. For a
|
||||||
|
* combo card with one I/O function and one memory card, there will be
|
||||||
|
* two of these structures allocated. Each card slot has such a list
|
||||||
|
* of sdmmc_function structures.
|
||||||
|
*/
|
||||||
|
struct sdmmc_function {
|
||||||
|
/* common members */
|
||||||
|
u_int16_t rca; /* relative card address */
|
||||||
|
int flags;
|
||||||
|
#define SFF_ERROR 0x0001 /* function is poo; ignore it */
|
||||||
|
#define SFF_SDHC 0x0002 /* SD High Capacity card */
|
||||||
|
/* SD card I/O function members */
|
||||||
|
int number; /* I/O function number or -1 */
|
||||||
|
struct sdmmc_cis cis; /* decoded CIS */
|
||||||
|
/* SD/MMC memory card members */
|
||||||
|
struct sdmmc_csd csd; /* decoded CSD value */
|
||||||
|
struct sdmmc_cid cid; /* decoded CID value */
|
||||||
|
sdmmc_response raw_cid; /* temp. storage for decoding */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SDMMC_LOCK(sc) lockmgr(&(sc)->sc_lock, LK_EXCLUSIVE, NULL)
|
||||||
|
#define SDMMC_UNLOCK(sc) lockmgr(&(sc)->sc_lock, LK_RELEASE, NULL)
|
||||||
|
#define SDMMC_ASSERT_LOCKED(sc) \
|
||||||
|
KASSERT(lockstatus(&((sc))->sc_lock) == LK_EXCLUSIVE)
|
||||||
|
|
||||||
|
#define SDMMC_DEFAULT_CLOCK 25000
|
||||||
|
#define SDMMC_DEFAULT_BLOCKLEN 512
|
||||||
|
|
||||||
|
#define SDMMC_NO_CARD 1
|
||||||
|
#define SDMMC_NEW_CARD 2
|
||||||
|
#define SDMMC_INSERTED 3
|
||||||
|
|
||||||
|
/* MMC commands */ /* response type */
|
||||||
|
#define MMC_GO_IDLE_STATE 0 /* R0 */
|
||||||
|
#define MMC_SEND_OP_COND 1 /* R3 */
|
||||||
|
#define MMC_ALL_SEND_CID 2 /* R2 */
|
||||||
|
#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
|
||||||
|
#define MMC_SWITCH 6 /* R1B */
|
||||||
|
#define MMC_SELECT_CARD 7 /* R1 */
|
||||||
|
#define MMC_SEND_EXT_CSD 8 /* R1 */
|
||||||
|
#define MMC_SEND_CSD 9 /* R2 */
|
||||||
|
#define MMC_STOP_TRANSMISSION 12 /* R1B */
|
||||||
|
#define MMC_SEND_STATUS 13 /* R1 */
|
||||||
|
#define MMC_SET_BLOCKLEN 16 /* R1 */
|
||||||
|
#define MMC_READ_BLOCK_SINGLE 17 /* R1 */
|
||||||
|
#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
|
||||||
|
#define MMC_SET_BLOCK_COUNT 23 /* R1 */
|
||||||
|
#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
|
||||||
|
#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */
|
||||||
|
#define MMC_APP_CMD 55 /* R1 */
|
||||||
|
|
||||||
|
/* SD commands */ /* response type */
|
||||||
|
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
|
||||||
|
#define SD_SWITCH_FUNC 6 /* R1 */
|
||||||
|
#define SD_SEND_IF_COND 8 /* R7 */
|
||||||
|
|
||||||
|
/* SD application commands */ /* response type */
|
||||||
|
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
|
||||||
|
#define SD_APP_OP_COND 41 /* R3 */
|
||||||
|
|
||||||
|
/* OCR bits */
|
||||||
|
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
|
||||||
|
#define MMC_OCR_3_5V_3_6V (1<<23)
|
||||||
|
#define MMC_OCR_3_4V_3_5V (1<<22)
|
||||||
|
#define MMC_OCR_3_3V_3_4V (1<<21)
|
||||||
|
#define MMC_OCR_3_2V_3_3V (1<<20)
|
||||||
|
#define MMC_OCR_3_1V_3_2V (1<<19)
|
||||||
|
#define MMC_OCR_3_0V_3_1V (1<<18)
|
||||||
|
#define MMC_OCR_2_9V_3_0V (1<<17)
|
||||||
|
#define MMC_OCR_2_8V_2_9V (1<<16)
|
||||||
|
#define MMC_OCR_2_7V_2_8V (1<<15)
|
||||||
|
#define MMC_OCR_2_6V_2_7V (1<<14)
|
||||||
|
#define MMC_OCR_2_5V_2_6V (1<<13)
|
||||||
|
#define MMC_OCR_2_4V_2_5V (1<<12)
|
||||||
|
#define MMC_OCR_2_3V_2_4V (1<<11)
|
||||||
|
#define MMC_OCR_2_2V_2_3V (1<<10)
|
||||||
|
#define MMC_OCR_2_1V_2_2V (1<<9)
|
||||||
|
#define MMC_OCR_2_0V_2_1V (1<<8)
|
||||||
|
#define MMC_OCR_1_9V_2_0V (1<<7)
|
||||||
|
#define MMC_OCR_1_8V_1_9V (1<<6)
|
||||||
|
#define MMC_OCR_1_7V_1_8V (1<<5)
|
||||||
|
#define MMC_OCR_1_6V_1_7V (1<<4)
|
||||||
|
|
||||||
|
#define SD_OCR_SDHC_CAP (1<<30)
|
||||||
|
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
|
||||||
|
|
||||||
|
/* R1 response type bits */
|
||||||
|
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
|
||||||
|
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
|
||||||
|
|
||||||
|
/* 48-bit response decoding (32 bits w/o CRC) */
|
||||||
|
#define MMC_R1(resp) ((resp)[0])
|
||||||
|
#define MMC_R3(resp) ((resp)[0])
|
||||||
|
#define SD_R6(resp) ((resp)[0])
|
||||||
|
|
||||||
|
/* RCA argument and response */
|
||||||
|
#define MMC_ARG_RCA(rca) ((rca) << 16)
|
||||||
|
#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
|
||||||
|
|
||||||
|
/* bus width argument */
|
||||||
|
#define SD_ARG_BUS_WIDTH_1 0
|
||||||
|
#define SD_ARG_BUS_WIDTH_4 2
|
||||||
|
|
||||||
|
/* MMC R2 response (CSD) */
|
||||||
|
#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
|
||||||
|
#define MMC_CSD_CSDVER_1_0 1
|
||||||
|
#define MMC_CSD_CSDVER_2_0 2
|
||||||
|
#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
|
||||||
|
#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
|
||||||
|
#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
|
||||||
|
#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */
|
||||||
|
#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */
|
||||||
|
#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */
|
||||||
|
#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
|
||||||
|
#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
|
||||||
|
#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
|
||||||
|
(MMC_CSD_C_SIZE_MULT((resp))+2))
|
||||||
|
#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
|
||||||
|
|
||||||
|
/* MMC v1 R2 response (CID) */
|
||||||
|
#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)
|
||||||
|
#define MMC_CID_PNM_V1_CPY(resp, pnm) \
|
||||||
|
do { \
|
||||||
|
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||||
|
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||||
|
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||||
|
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||||
|
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||||
|
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
|
||||||
|
(pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \
|
||||||
|
(pnm)[7] = '\0'; \
|
||||||
|
} while (0)
|
||||||
|
#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8)
|
||||||
|
#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24)
|
||||||
|
#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8)
|
||||||
|
|
||||||
|
/* MMC v2 R2 response (CID) */
|
||||||
|
#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8)
|
||||||
|
#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16)
|
||||||
|
#define MMC_CID_PNM_V2_CPY(resp, pnm) \
|
||||||
|
do { \
|
||||||
|
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||||
|
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||||
|
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||||
|
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||||
|
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||||
|
(pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
|
||||||
|
(pnm)[6] = '\0'; \
|
||||||
|
} while (0)
|
||||||
|
#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32)
|
||||||
|
|
||||||
|
/* SD R2 response (CSD) */
|
||||||
|
#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
|
||||||
|
#define SD_CSD_CSDVER_1_0 0
|
||||||
|
#define SD_CSD_CSDVER_2_0 1
|
||||||
|
#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
|
||||||
|
#define SD_CSD_TAAC_1_5_MSEC 0x26
|
||||||
|
#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
|
||||||
|
#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
|
||||||
|
#define SD_CSD_SPEED_25_MHZ 0x32
|
||||||
|
#define SD_CSD_SPEED_50_MHZ 0x5a
|
||||||
|
#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
|
||||||
|
#define SD_CSD_CCC_ALL 0x5f5
|
||||||
|
#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
|
||||||
|
#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1)
|
||||||
|
#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1)
|
||||||
|
#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1)
|
||||||
|
#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1)
|
||||||
|
#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
|
||||||
|
#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
|
||||||
|
(SD_CSD_C_SIZE_MULT((resp))+2))
|
||||||
|
#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22)
|
||||||
|
#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10)
|
||||||
|
#define SD_CSD_V2_BL_LEN 0x9 /* 512 */
|
||||||
|
#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
|
||||||
|
#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
|
||||||
|
#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3)
|
||||||
|
#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3)
|
||||||
|
#define SD_CSD_VDD_RW_CURR_100mA 0x7
|
||||||
|
#define SD_CSD_VDD_RW_CURR_80mA 0x6
|
||||||
|
#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
|
||||||
|
#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1)
|
||||||
|
#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */
|
||||||
|
#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */
|
||||||
|
#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1)
|
||||||
|
#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
|
||||||
|
#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
|
||||||
|
#define SD_CSD_RW_BL_LEN_2G 0xa
|
||||||
|
#define SD_CSD_RW_BL_LEN_1G 0x9
|
||||||
|
#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
|
||||||
|
#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1)
|
||||||
|
#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1)
|
||||||
|
#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1)
|
||||||
|
#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1)
|
||||||
|
#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2)
|
||||||
|
|
||||||
|
/* SD R2 response (CID) */
|
||||||
|
#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8)
|
||||||
|
#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16)
|
||||||
|
#define SD_CID_PNM_CPY(resp, pnm) \
|
||||||
|
do { \
|
||||||
|
(pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
|
||||||
|
(pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
|
||||||
|
(pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
|
||||||
|
(pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
|
||||||
|
(pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
|
||||||
|
(pnm)[5] = '\0'; \
|
||||||
|
} while (0)
|
||||||
|
#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8)
|
||||||
|
#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
|
||||||
|
#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
|
||||||
|
|
||||||
|
/* Might be slow, but it should work on big and little endian systems. */
|
||||||
|
#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len))
|
||||||
|
static __inline int
|
||||||
|
__bitfield(u_int32_t *src, int start, int len)
|
||||||
|
{
|
||||||
|
u_int8_t *sp;
|
||||||
|
u_int32_t dst, mask;
|
||||||
|
int shift, bs, bc;
|
||||||
|
|
||||||
|
if (start < 0 || len < 0 || len > 32)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dst = 0;
|
||||||
|
mask = len % 32 ? UINT_MAX >> (32 - (len % 32)) : UINT_MAX;
|
||||||
|
shift = 0;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
sp = (u_int8_t *)src + start / 8;
|
||||||
|
bs = start % 8;
|
||||||
|
bc = 8 - bs;
|
||||||
|
if (bc > len)
|
||||||
|
bc = len;
|
||||||
|
dst |= (*sp++ >> bs) << shift;
|
||||||
|
shift += bc;
|
||||||
|
start += bc;
|
||||||
|
len -= bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst &= mask;
|
||||||
|
return (int)dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
149
stage2/sha.c
Normal file
149
stage2/sha.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
SHA-1 in C
|
||||||
|
By Steve Reid <steve@edmweb.com>
|
||||||
|
100% Public Domain
|
||||||
|
Test Vectors (from FIPS PUB 180-1)
|
||||||
|
"abc"
|
||||||
|
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||||
|
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||||
|
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||||
|
A million repetitions of "a"
|
||||||
|
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "sha.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "latte.h"
|
||||||
|
|
||||||
|
//should be divisible by four
|
||||||
|
#define BLOCKSIZE 32
|
||||||
|
|
||||||
|
#define SHA_CMD_FLAG_EXEC (1<<31)
|
||||||
|
#define SHA_CMD_FLAG_IRQ (1<<30)
|
||||||
|
#define SHA_CMD_FLAG_ERR (1<<29)
|
||||||
|
#define SHA_CMD_AREA_BLOCK ((1<<10) - 1)
|
||||||
|
|
||||||
|
static void sha_transform(u32 state[SHA_HASH_WORDS], u8 buffer[SHA_BLOCK_SIZE], u32 blocks)
|
||||||
|
{
|
||||||
|
if(blocks == 0) return;
|
||||||
|
|
||||||
|
/* Copy ctx->state[] to working vars */
|
||||||
|
write32(SHA_H0, state[0]);
|
||||||
|
write32(SHA_H1, state[1]);
|
||||||
|
write32(SHA_H2, state[2]);
|
||||||
|
write32(SHA_H3, state[3]);
|
||||||
|
write32(SHA_H4, state[4]);
|
||||||
|
|
||||||
|
// assign block to local copy which is 64-byte aligned
|
||||||
|
u8 *block = memalign(64, SHA_BLOCK_SIZE * blocks);
|
||||||
|
memcpy(block, buffer, SHA_BLOCK_SIZE * blocks);
|
||||||
|
|
||||||
|
// royal flush :)
|
||||||
|
dc_flushrange(block, SHA_BLOCK_SIZE * blocks);
|
||||||
|
ahb_flush_to(RB_SHA);
|
||||||
|
|
||||||
|
// tell sha1 controller the block source address
|
||||||
|
write32(SHA_SRC, dma_addr(block));
|
||||||
|
|
||||||
|
// tell sha1 controller number of blocks
|
||||||
|
write32(SHA_CTRL, (read32(SHA_CTRL) & ~(SHA_CMD_AREA_BLOCK)) | (blocks - 1));
|
||||||
|
|
||||||
|
// fire up hashing and wait till its finished
|
||||||
|
write32(SHA_CTRL, read32(SHA_CTRL) | SHA_CMD_FLAG_EXEC);
|
||||||
|
while (read32(SHA_CTRL) & SHA_CMD_FLAG_EXEC);
|
||||||
|
|
||||||
|
// free the aligned data
|
||||||
|
free(block);
|
||||||
|
|
||||||
|
/* Add the working vars back into ctx.state[] */
|
||||||
|
state[0] = read32(SHA_H0);
|
||||||
|
state[1] = read32(SHA_H1);
|
||||||
|
state[2] = read32(SHA_H2);
|
||||||
|
state[3] = read32(SHA_H3);
|
||||||
|
state[4] = read32(SHA_H4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha_init(sha_ctx* ctx)
|
||||||
|
{
|
||||||
|
memset(ctx, 0, sizeof(sha_ctx));
|
||||||
|
|
||||||
|
ctx->state[0] = 0x67452301;
|
||||||
|
ctx->state[1] = 0xEFCDAB89;
|
||||||
|
ctx->state[2] = 0x98BADCFE;
|
||||||
|
ctx->state[3] = 0x10325476;
|
||||||
|
ctx->state[4] = 0xC3D2E1F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha_update(sha_ctx* ctx, const void* inbuf, size_t size)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
u8* data = (u8*)inbuf;
|
||||||
|
|
||||||
|
j = (ctx->count[0] >> 3) & 63;
|
||||||
|
if ((ctx->count[0] += size << 3) < (size << 3))
|
||||||
|
ctx->count[1]++;
|
||||||
|
ctx->count[1] += (size >> 29);
|
||||||
|
if ((j + size) > 63) {
|
||||||
|
memcpy(&ctx->buffer[j], data, (i = 64-j));
|
||||||
|
sha_transform(ctx->state, ctx->buffer, 1);
|
||||||
|
// try bigger blocks at once
|
||||||
|
for ( ; i + 63 + ((BLOCKSIZE-1)*64) < size; i += (64 + (BLOCKSIZE-1)*64)) {
|
||||||
|
sha_transform(ctx->state, &data[i], BLOCKSIZE);
|
||||||
|
}
|
||||||
|
for ( ; i + 63 + (((BLOCKSIZE/2)-1)*64) < size; i += (64 + ((BLOCKSIZE/2)-1)*64)) {
|
||||||
|
sha_transform(ctx->state, &data[i], BLOCKSIZE/2);
|
||||||
|
}
|
||||||
|
for ( ; i + 63 + (((BLOCKSIZE/4)-1)*64) < size; i += (64 + ((BLOCKSIZE/4)-1)*64)) {
|
||||||
|
sha_transform(ctx->state, &data[i], BLOCKSIZE/4);
|
||||||
|
}
|
||||||
|
for ( ; i + 63 < size; i += 64) {
|
||||||
|
sha_transform(ctx->state, &data[i], 1);
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
else i = 0;
|
||||||
|
memcpy(&ctx->buffer[j], &data[i], size - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha_final(sha_ctx* ctx, void* outbuf)
|
||||||
|
{
|
||||||
|
u8 final_count[8];
|
||||||
|
u8* digest = outbuf;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
final_count[i] = ((ctx->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||||
|
}
|
||||||
|
|
||||||
|
sha_update(ctx, "\200", 1);
|
||||||
|
while ((ctx->count[0] & 504) != 448) {
|
||||||
|
sha_update(ctx, "\0", 1);
|
||||||
|
}
|
||||||
|
sha_update(ctx, final_count, sizeof(final_count)); /* Should cause a sha_transform() */
|
||||||
|
for (int i = 0; i < SHA_HASH_SIZE; i++) {
|
||||||
|
digest[i] = ((ctx->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wipe variables */
|
||||||
|
memset(ctx->buffer, 0, sizeof(ctx->buffer));
|
||||||
|
memset(ctx->state, 0, sizeof(ctx->state));
|
||||||
|
memset(ctx->count, 0, sizeof(ctx->count));
|
||||||
|
memset(final_count, 0, sizeof(final_count));
|
||||||
|
sha_transform(ctx->state, ctx->buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha_hash(const void* inbuf, void* outbuf, size_t size)
|
||||||
|
{
|
||||||
|
sha_ctx ctx;
|
||||||
|
|
||||||
|
sha_init(&ctx);
|
||||||
|
sha_update(&ctx, inbuf, size);
|
||||||
|
sha_final(&ctx, outbuf);
|
||||||
|
}
|
36
stage2/sha.h
Normal file
36
stage2/sha.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SHA_H
|
||||||
|
#define _SHA_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define SHA_BLOCK_BITS (0x200) // 512
|
||||||
|
#define SHA_BLOCK_SIZE (SHA_BLOCK_BITS / 8) // 64
|
||||||
|
#define SHA_BLOCK_WORDS (SHA_BLOCK_SIZE / sizeof(u32)) // 16
|
||||||
|
|
||||||
|
#define SHA_HASH_BITS (0xA0) // 160
|
||||||
|
#define SHA_HASH_SIZE (SHA_HASH_BITS / 8) // 20 (0x14)
|
||||||
|
#define SHA_HASH_WORDS (SHA_HASH_SIZE / sizeof(u32)) // 5
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 state[SHA_HASH_WORDS];
|
||||||
|
u32 count[2];
|
||||||
|
u8 buffer[SHA_BLOCK_SIZE];
|
||||||
|
} sha_ctx;
|
||||||
|
|
||||||
|
void sha_init(sha_ctx* ctx);
|
||||||
|
void sha_update(sha_ctx* ctx, const void* inbuf, size_t size);
|
||||||
|
void sha_final(sha_ctx* ctx, void* outbuf);
|
||||||
|
|
||||||
|
void sha_hash(const void* inbuf, void* outbuf, size_t size);
|
||||||
|
|
||||||
|
#endif
|
118
stage2/smc.c
Normal file
118
stage2/smc.c
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "latte.h"
|
||||||
|
|
||||||
|
void SRAM_TEXT __attribute__((__noreturn__)) smc_shutdown(bool reset)
|
||||||
|
{
|
||||||
|
write16(MEM_FLUSH_MASK, 0b1111);
|
||||||
|
while(read16(MEM_FLUSH_MASK) & 0b1111);
|
||||||
|
|
||||||
|
if(read32(LT_RESETS) & 4) {
|
||||||
|
write32(LT_ABIF_CPLTL_OFFSET, 0xC0008020);
|
||||||
|
write32(LT_ABIF_CPLTL_DATA, 0xFFFFFFFF);
|
||||||
|
write32(LT_ABIF_CPLTL_OFFSET, 0xC0000E60);
|
||||||
|
write32(LT_ABIF_CPLTL_DATA, 0xFFFFFFDB);
|
||||||
|
}
|
||||||
|
|
||||||
|
write32(LT_RESETS_AHB, 0xFFFFCE71);
|
||||||
|
write32(LT_RESETS_AHMN, 0xFFFFCD70);
|
||||||
|
write32(LT_RESETS_COMPAT, 0xFF8FCDEF);
|
||||||
|
|
||||||
|
write16(MEM_REFRESH_FLAG, 0);
|
||||||
|
|
||||||
|
write16(MEM_SEQ_REG_ADDR, 0x18);
|
||||||
|
write16(MEM_SEQ_REG_VAL, 1);
|
||||||
|
write16(MEM_SEQ_REG_ADDR, 0x19);
|
||||||
|
write16(MEM_SEQ_REG_VAL, 0);
|
||||||
|
write16(MEM_SEQ_REG_ADDR, 0x1A);
|
||||||
|
write16(MEM_SEQ_REG_VAL, 1);
|
||||||
|
|
||||||
|
write16(MEM_SEQ0_REG_ADDR, 0x18);
|
||||||
|
write16(MEM_SEQ0_REG_VAL, 1);
|
||||||
|
write16(MEM_SEQ0_REG_ADDR, 0x19);
|
||||||
|
write16(MEM_SEQ0_REG_VAL, 0);
|
||||||
|
write16(MEM_SEQ0_REG_ADDR, 0x1A);
|
||||||
|
write16(MEM_SEQ0_REG_VAL, 1);
|
||||||
|
|
||||||
|
{
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0xA1000100);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reset) {
|
||||||
|
{
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0xA1000D00);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0x501);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear32(LT_RESETS, 1);
|
||||||
|
} else {
|
||||||
|
{
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0xA1000D00);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0x101);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0xA1000D00);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0x108);
|
||||||
|
write32(EXI0_DATA, 0x10101);
|
||||||
|
write32(EXI0_CR, 0x35);
|
||||||
|
while(!(read32(EXI0_CSR) & 8));
|
||||||
|
|
||||||
|
write32(EXI0_CSR, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((__noreturn__)) smc_power_off(void)
|
||||||
|
{
|
||||||
|
smc_shutdown(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((__noreturn__)) smc_reset(void)
|
||||||
|
{
|
||||||
|
smc_shutdown(true);
|
||||||
|
}
|
20
stage2/smc.h
Normal file
20
stage2/smc.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SMC_H
|
||||||
|
#define _SMC_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
void __attribute__((__noreturn__)) smc_shutdown(bool reset);
|
||||||
|
void __attribute__((__noreturn__)) smc_reset(void);
|
||||||
|
void __attribute__((__noreturn__)) smc_power_off(void);
|
||||||
|
|
||||||
|
#endif
|
83
stage2/start.S
Normal file
83
stage2/start.S
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
.arm
|
||||||
|
|
||||||
|
.extern _main
|
||||||
|
.extern __got_start
|
||||||
|
.extern __got_end
|
||||||
|
.extern __bss_start
|
||||||
|
.extern __bss_end
|
||||||
|
.extern __stack_addr
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
.extern v_irq
|
||||||
|
|
||||||
|
.section .init
|
||||||
|
|
||||||
|
_vectors:
|
||||||
|
_start:
|
||||||
|
ldr pc, =v_reset
|
||||||
|
1: b 1b
|
||||||
|
2: b 2b
|
||||||
|
3: b 3b
|
||||||
|
4: b 4b
|
||||||
|
5: b 5b
|
||||||
|
ldr pc, =v_irq
|
||||||
|
6: b 6b
|
||||||
|
|
||||||
|
.pool
|
||||||
|
|
||||||
|
v_reset:
|
||||||
|
@ Switch to System mode
|
||||||
|
msr cpsr_c, #0xdf
|
||||||
|
|
||||||
|
@ Get loader base from ELF loader
|
||||||
|
mov r4, r0
|
||||||
|
|
||||||
|
@ Set up a stack
|
||||||
|
ldr sp, =__stack_addr
|
||||||
|
|
||||||
|
@ clear the stack to a marker value
|
||||||
|
ldr r1, =__stack_end
|
||||||
|
ldr r2, =__stack_addr
|
||||||
|
ldr r3, =0xDEADBEEF
|
||||||
|
stk_loop:
|
||||||
|
@ check for the end
|
||||||
|
cmp r1, r2
|
||||||
|
beq done_stk
|
||||||
|
@ clear the word and move on
|
||||||
|
str r3, [r1]
|
||||||
|
add r1, r1, #4
|
||||||
|
b stk_loop
|
||||||
|
|
||||||
|
done_stk:
|
||||||
|
@ clear BSS
|
||||||
|
ldr r1, =__bss_start
|
||||||
|
ldr r2, =__bss_end
|
||||||
|
mov r3, #0
|
||||||
|
bss_loop:
|
||||||
|
@ check for the end
|
||||||
|
cmp r1, r2
|
||||||
|
beq done_bss
|
||||||
|
@ clear the word and move on
|
||||||
|
str r3, [r1]
|
||||||
|
add r1, r1, #4
|
||||||
|
b bss_loop
|
||||||
|
|
||||||
|
done_bss:
|
||||||
|
@ take the plunge
|
||||||
|
mov r0, r4
|
||||||
|
ldr r1, =_main
|
||||||
|
blx r1
|
||||||
|
@ _main returned! Go to whatever address it returned...
|
||||||
|
bx r0
|
||||||
|
|
||||||
|
.pool
|
||||||
|
|
68
stage2/types.h
Normal file
68
stage2/types.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TYPES_H__
|
||||||
|
#define __TYPES_H__
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
|
||||||
|
typedef int8_t s8;
|
||||||
|
typedef int16_t s16;
|
||||||
|
typedef int32_t s32;
|
||||||
|
typedef int64_t s64;
|
||||||
|
|
||||||
|
typedef volatile u8 vu8;
|
||||||
|
typedef volatile u16 vu16;
|
||||||
|
typedef volatile u32 vu32;
|
||||||
|
typedef volatile u64 vu64;
|
||||||
|
|
||||||
|
typedef volatile s8 vs8;
|
||||||
|
typedef volatile s16 vs16;
|
||||||
|
typedef volatile s32 vs32;
|
||||||
|
typedef volatile s64 vs64;
|
||||||
|
|
||||||
|
#define NULL ((void *)0)
|
||||||
|
|
||||||
|
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||||
|
#define SRAM_TEXT __attribute__((section(".sram.text")))
|
||||||
|
#define SRAM_DATA __attribute__((section(".sram.data")))
|
||||||
|
#define NORETURN __attribute__((__noreturn__))
|
||||||
|
|
||||||
|
#define ALIGNED(x) __attribute__((aligned(x)))
|
||||||
|
#define PACKED __attribute__((packed))
|
||||||
|
|
||||||
|
#define STACK_ALIGN(type, name, cnt, alignment) \
|
||||||
|
u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + \
|
||||||
|
(((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - \
|
||||||
|
((sizeof(type)*(cnt))%(alignment))) : 0))]; \
|
||||||
|
type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (( \
|
||||||
|
(u32)(_al__##name))&((alignment)-1))))
|
||||||
|
|
||||||
|
|
||||||
|
#define INT_MAX ((s32)0x7fffffff)
|
||||||
|
#define UINT_MAX ((u32)0xffffffff)
|
||||||
|
|
||||||
|
//#define LONG_MAX INT_MAX
|
||||||
|
//#define ULONG_MAX UINT_MAX
|
||||||
|
|
||||||
|
//#define LLONG_MAX ((s64)0x7fffffffffffffff)
|
||||||
|
//#define ULLONG_MAX ((u64)0xffffffffffffffff)
|
||||||
|
|
||||||
|
#endif
|
52
stage2/utils.c
Normal file
52
stage2/utils.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 SALT
|
||||||
|
* Copyright (C) 2016 Daz Jones <daz@dazzozo.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "latte.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
void udelay(u32 d)
|
||||||
|
{
|
||||||
|
// should be good to max .2% error
|
||||||
|
u32 ticks = d * 19 / 10;
|
||||||
|
|
||||||
|
if(ticks < 2)
|
||||||
|
ticks = 2;
|
||||||
|
|
||||||
|
u32 now = read32(LT_TIMER);
|
||||||
|
|
||||||
|
u32 then = now + ticks;
|
||||||
|
|
||||||
|
if(then < now) {
|
||||||
|
while(read32(LT_TIMER) >= now);
|
||||||
|
now = read32(LT_TIMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(now < then) {
|
||||||
|
now = read32(LT_TIMER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void panic(u8 v)
|
||||||
|
{
|
||||||
|
while(true) {
|
||||||
|
//debug_output(v);
|
||||||
|
//set32(HW_GPIO1BOUT, GP_SLOTLED);
|
||||||
|
//udelay(500000);
|
||||||
|
//debug_output(0);
|
||||||
|
//clear32(HW_GPIO1BOUT, GP_SLOTLED);
|
||||||
|
//udelay(500000);
|
||||||
|
}
|
||||||
|
}
|
213
stage2/utils.h
Normal file
213
stage2/utils.h
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __UTILS_H__
|
||||||
|
#define __UTILS_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u32 read32(u32 addr)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ("ldr\t%0, [%1]" : "=l" (data) : "l" (addr));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE void write32(u32 addr, u32 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("str\t%0, [%1]" : : "l" (data), "l" (addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u32 set32(u32 addr, u32 set)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldr\t%0, [%1]\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstr\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (set)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u32 clear32(u32 addr, u32 clear)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldr\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %2\n"
|
||||||
|
"\tstr\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u32 mask32(u32 addr, u32 clear, u32 set)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldr\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %3\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstr\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (set), "l" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u16 read16(u32 addr)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ("ldrh\t%0, [%1]" : "=l" (data) : "l" (addr));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE void write16(u32 addr, u16 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("strh\t%0, [%1]" : : "l" (data), "l" (addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u16 set16(u32 addr, u16 set)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrh\t%0, [%1]\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrh\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (set)
|
||||||
|
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u16 clear16(u32 addr, u16 clear)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrh\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %2\n"
|
||||||
|
"\tstrh\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u16 mask16(u32 addr, u16 clear, u16 set)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrh\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %3\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrh\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (set), "l" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u8 read8(u32 addr)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ("ldrb\t%0, [%1]" : "=l" (data) : "l" (addr));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE void write8(u32 addr, u8 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("strb\t%0, [%1]" : : "l" (data), "l" (addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u8 set8(u32 addr, u8 set)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrb\t%0, [%1]\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrb\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (set)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u8 clear8(u32 addr, u8 clear)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrb\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %2\n"
|
||||||
|
"\tstrb\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u8 mask8(u32 addr, u8 clear, u8 set)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrb\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %3\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrb\t%0, [%1]"
|
||||||
|
: "=&l" (data)
|
||||||
|
: "l" (addr), "l" (set), "l" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These functions are guaranteed to copy by reading from src and writing to dst in <n>-bit units
|
||||||
|
* If size is not aligned, the remaining bytes are not copied
|
||||||
|
*/
|
||||||
|
void memset32(void *dst, u32 value, u32 size);
|
||||||
|
void memcpy32(void *dst, void *src, u32 size);
|
||||||
|
void memset16(void *dst, u16 value, u32 size);
|
||||||
|
void memcpy16(void *dst, void *src, u32 size);
|
||||||
|
void memset8(void *dst, u8 value, u32 size);
|
||||||
|
void memcpy8(void *dst, void *src, u32 size);
|
||||||
|
|
||||||
|
void udelay(u32 d);
|
||||||
|
void panic(u8 v);
|
||||||
|
|
||||||
|
static inline ALWAYS_INLINE u32 get_cpsr(void)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ( "mrs\t%0, cpsr" : "=r" (data) );
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STACK_ALIGN(type, name, cnt, alignment) \
|
||||||
|
u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + \
|
||||||
|
(((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - \
|
||||||
|
((sizeof(type)*(cnt))%(alignment))) : 0))]; \
|
||||||
|
type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (( \
|
||||||
|
(u32)(_al__##name))&((alignment)-1))))
|
||||||
|
|
||||||
|
|
||||||
|
#define max(a, b) \
|
||||||
|
({ __typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a > _b ? _a : _b; })
|
||||||
|
|
||||||
|
#define min(a, b) \
|
||||||
|
({ __typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a < _b ? _a : _b; })
|
||||||
|
|
||||||
|
#endif
|
70
stage2/utils_asm.S
Normal file
70
stage2/utils_asm.S
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* minute - a port of the "mini" IOS replacement for the Wii U.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||||
|
*
|
||||||
|
* This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
* see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
.arm
|
||||||
|
|
||||||
|
.globl memcpy32
|
||||||
|
.globl memcpy16
|
||||||
|
.globl memcpy8
|
||||||
|
.globl memset32
|
||||||
|
.globl memset16
|
||||||
|
.globl memset8
|
||||||
|
|
||||||
|
.section .sram.text
|
||||||
|
|
||||||
|
memcpy32:
|
||||||
|
bics r2, #3
|
||||||
|
bxeq lr
|
||||||
|
1: ldr r3, [r1],#4
|
||||||
|
str r3, [r0],#4
|
||||||
|
subs r2, #4
|
||||||
|
bne 1b
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
memset32:
|
||||||
|
bics r2, #3
|
||||||
|
bxeq lr
|
||||||
|
1: str r1, [r0],#4
|
||||||
|
subs r2, #4
|
||||||
|
bne 1b
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
memcpy16:
|
||||||
|
bics r2, #1
|
||||||
|
bxeq lr
|
||||||
|
1: ldrh r3, [r1],#2
|
||||||
|
strh r3, [r0],#2
|
||||||
|
subs r2, #2
|
||||||
|
bne 1b
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
memset16:
|
||||||
|
bics r2, #1
|
||||||
|
bxeq lr
|
||||||
|
1: strh r1, [r0],#2
|
||||||
|
subs r2, #2
|
||||||
|
bne 1b
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
memcpy8:
|
||||||
|
cmp r2, #0
|
||||||
|
bxeq lr
|
||||||
|
1: ldrb r3, [r1],#1
|
||||||
|
strb r3, [r0],#1
|
||||||
|
subs r2, #1
|
||||||
|
bne 1b
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
memset8:
|
||||||
|
cmp r2, #0
|
||||||
|
bxeq lr
|
||||||
|
1: strb r1, [r0],#1
|
||||||
|
subs r2, #1
|
||||||
|
bne 1b
|
||||||
|
bx lr
|
6
stage2ldr/.gitignore
vendored
Normal file
6
stage2ldr/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
*.d
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.o
|
||||||
|
*.map
|
||||||
|
|
36
stage2ldr/Makefile
Normal file
36
stage2ldr/Makefile
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
C_FILES := $(foreach dir, ., $(wildcard $(dir)/*.c))
|
||||||
|
S_FILES := $(foreach dir, ., $(wildcard $(dir)/*.S))
|
||||||
|
|
||||||
|
OBJS := $(C_FILES:.c=.o) $(S_FILES:.S=.o)
|
||||||
|
|
||||||
|
PREFIX = $(DEVKITARM)/bin/arm-none-eabi
|
||||||
|
CC = $(PREFIX)-gcc
|
||||||
|
LD = $(PREFIX)-ld
|
||||||
|
STRIP = $(PREFIX)-strip
|
||||||
|
OBJCOPY = $(PREFIX)-objcopy
|
||||||
|
|
||||||
|
ASFLAGS = -marm -fomit-frame-pointer -mbig-endian -fshort-wchar -mcpu=arm926ej-s -march=armv5te -fno-zero-initialized-in-bss
|
||||||
|
CFLAGS = -Os -Wall -fpic $(ASFLAGS)
|
||||||
|
LDFLAGS = -n -nostartfiles -Wl,-EB -L"$(DEVKITARM)/arm-none-eabi/lib"
|
||||||
|
|
||||||
|
all: stage2ldr.bin
|
||||||
|
|
||||||
|
stage2ldr.bin: stage2ldr.elf
|
||||||
|
@echo $(notdir $@)
|
||||||
|
@$(OBJCOPY) -S -O binary $^ $@
|
||||||
|
|
||||||
|
stage2ldr.elf: $(OBJS)
|
||||||
|
@echo $(notdir $@)
|
||||||
|
@$(CC) $(LDFLAGS) -T link.ld $^ -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CC) $(CFLAGS) -c $^ -o $@
|
||||||
|
|
||||||
|
%.o: %.S
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(CC) $(ASFLAGS) -c $^ -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf $(OBJS) stage2ldr.elf stage2ldr.bin
|
||||||
|
@echo "Cleaned!"
|
66
stage2ldr/elf.h
Normal file
66
stage2ldr/elf.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: ELF structures
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef __ELF_H__
|
||||||
|
#define __ELF_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define EI_NIDENT 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char e_ident[EI_NIDENT];
|
||||||
|
u16 e_type;
|
||||||
|
u16 e_machine;
|
||||||
|
u32 e_version;
|
||||||
|
void *e_entry;
|
||||||
|
u32 e_phoff;
|
||||||
|
u32 e_shoff;
|
||||||
|
u32 e_flags;
|
||||||
|
u16 e_ehsize;
|
||||||
|
u16 e_phentsize;
|
||||||
|
u16 e_phnum;
|
||||||
|
u16 e_shentsize;
|
||||||
|
u16 e_shnum;
|
||||||
|
u16 e_shtrndx;
|
||||||
|
} Elf32_Ehdr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 p_type;
|
||||||
|
u32 p_offset;
|
||||||
|
void *p_vaddr;
|
||||||
|
void *p_paddr;
|
||||||
|
u32 p_filesz;
|
||||||
|
u32 p_memsz;
|
||||||
|
u32 p_flags;
|
||||||
|
u32 p_align;
|
||||||
|
} Elf32_Phdr;
|
||||||
|
|
||||||
|
#define PT_NULL 0
|
||||||
|
#define PT_LOAD 1
|
||||||
|
#define PT_DYNAMIC 2
|
||||||
|
#define PT_INTERP 3
|
||||||
|
#define PT_NOTE 4
|
||||||
|
#define PT_SHLIB 5
|
||||||
|
#define PT_PHDR 6
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
31
stage2ldr/hollywood.h
Normal file
31
stage2ldr/hollywood.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: Hollywood register definitions
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef __HOLLYWOOD_H__
|
||||||
|
#define __HOLLYWOOD_H__
|
||||||
|
|
||||||
|
#define HW_REG_BASE 0xd800000
|
||||||
|
#define HW_TIMER (HW_REG_BASE + 0x010)
|
||||||
|
#define HW_MEMMIRR (HW_REG_BASE + 0x060)
|
||||||
|
#define HW_BOOT0 (HW_REG_BASE + 0x18c)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
94
stage2ldr/link.ld
Normal file
94
stage2ldr/link.ld
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
elfloader - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
OUTPUT_FORMAT("elf32-bigarm")
|
||||||
|
OUTPUT_ARCH(arm)
|
||||||
|
EXTERN(_start)
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
__base_addr = 0;
|
||||||
|
__stack_size = 0x100;
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = __base_addr;
|
||||||
|
__code_start = .;
|
||||||
|
|
||||||
|
.init :
|
||||||
|
{
|
||||||
|
*(.init)
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.got :
|
||||||
|
{
|
||||||
|
__got_start = .;
|
||||||
|
*(.got.*)
|
||||||
|
*(.got)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__got_end = . ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
*(.text.*)
|
||||||
|
*(.gnu.warning)
|
||||||
|
*(.gnu.linkonce.t*)
|
||||||
|
*(.glue_7)
|
||||||
|
*(.glue_7t)
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
__text_end = . ;
|
||||||
|
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
*(.rodata)
|
||||||
|
*all.rodata*(*)
|
||||||
|
*(.roda)
|
||||||
|
*(.rodata.*)
|
||||||
|
*(.gnu.linkonce.r*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
*(.data)
|
||||||
|
*(.data.*)
|
||||||
|
*(.gnu.linkonce.d*)
|
||||||
|
*(.dynbss)
|
||||||
|
*(.gnu.linkonce.b*)
|
||||||
|
*(.bss*)
|
||||||
|
*(.sbss*)
|
||||||
|
*(COMMON)
|
||||||
|
|
||||||
|
. = ALIGN(32);
|
||||||
|
__stack_end = .;
|
||||||
|
. = . + __stack_size;
|
||||||
|
__stack_addr = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
__end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROVIDE (__stack_end = __stack_end);
|
||||||
|
PROVIDE (__stack_addr = __stack_addr);
|
||||||
|
PROVIDE (__got_start = __got_start);
|
||||||
|
PROVIDE (__got_end = __got_end);
|
||||||
|
PROVIDE (__elf_start = __end);
|
73
stage2ldr/loader.c
Normal file
73
stage2ldr/loader.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "start.h"
|
||||||
|
#include "hollywood.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
extern u8 __elf_start[];
|
||||||
|
|
||||||
|
void *loadelf(const u8 *elf) {
|
||||||
|
if(memcmp("\x7F" "ELF\x01\x02\x01",elf,7)) {
|
||||||
|
panic(0xE3);
|
||||||
|
}
|
||||||
|
|
||||||
|
Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf;
|
||||||
|
if(ehdr->e_phoff == 0) {
|
||||||
|
panic(0xE4);
|
||||||
|
}
|
||||||
|
int count = ehdr->e_phnum;
|
||||||
|
Elf32_Phdr *phdr = (Elf32_Phdr*)(elf + ehdr->e_phoff);
|
||||||
|
while(count--)
|
||||||
|
{
|
||||||
|
if(phdr->p_type == PT_LOAD) {
|
||||||
|
const void *src = elf + phdr->p_offset;
|
||||||
|
memcpy(phdr->p_paddr, src, phdr->p_filesz);
|
||||||
|
}
|
||||||
|
phdr++;
|
||||||
|
}
|
||||||
|
return ehdr->e_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void disable_boot0()
|
||||||
|
{
|
||||||
|
set32(HW_BOOT0, 0x1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mem_setswap()
|
||||||
|
{
|
||||||
|
set32(HW_MEMMIRR, 0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_main(void *base)
|
||||||
|
{
|
||||||
|
void *entry;
|
||||||
|
|
||||||
|
mem_setswap(1);
|
||||||
|
disable_boot0(1);
|
||||||
|
|
||||||
|
entry = loadelf(__elf_start);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
398
stage2ldr/math_asm.S
Normal file
398
stage2ldr/math_asm.S
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
#ifdef __ARMEB__
|
||||||
|
#define xh r0
|
||||||
|
#define xl r1
|
||||||
|
#define yh r2
|
||||||
|
#define yl r3
|
||||||
|
#else
|
||||||
|
#define xl r0
|
||||||
|
#define xh r1
|
||||||
|
#define yl r2
|
||||||
|
#define yh r3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.global __muldi3
|
||||||
|
__muldi3:
|
||||||
|
|
||||||
|
.global __aeabi_lmul
|
||||||
|
__aeabi_lmul:
|
||||||
|
|
||||||
|
mul xh, yl, xh
|
||||||
|
mla xh, xl, yh, xh
|
||||||
|
mov ip, xl, lsr #16
|
||||||
|
mov yh, yl, lsr #16
|
||||||
|
bic xl, xl, ip, lsl #16
|
||||||
|
bic yl, yl, yh, lsl #16
|
||||||
|
mla xh, yh, ip, xh
|
||||||
|
mul yh, xl, yh
|
||||||
|
mul xl, yl, xl
|
||||||
|
mul ip, yl, ip
|
||||||
|
adds xl, xl, yh, lsl #16
|
||||||
|
adc xh, xh, yh, lsr #16
|
||||||
|
adds xl, xl, ip, lsl #16
|
||||||
|
adc xh, xh, ip, lsr #16
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
|
||||||
|
dividend .req r0
|
||||||
|
divisor .req r1
|
||||||
|
result .req r2
|
||||||
|
curbit .req r3
|
||||||
|
.globl __udivsi3
|
||||||
|
.type __udivsi3 ,function
|
||||||
|
.globl __aeabi_uidiv
|
||||||
|
.type __aeabi_uidiv ,function
|
||||||
|
.align 0
|
||||||
|
__udivsi3:
|
||||||
|
__aeabi_uidiv:
|
||||||
|
cmp divisor, #0
|
||||||
|
beq Ldiv0_uidiv
|
||||||
|
mov curbit, #1
|
||||||
|
mov result, #0
|
||||||
|
cmp dividend, divisor
|
||||||
|
bcc Lgot_result
|
||||||
|
Loop1:
|
||||||
|
@ Unless the divisor is very big, shift it up in multiples of
|
||||||
|
@ four bits, since this is the amount of unwinding in the main
|
||||||
|
@ division loop. Continue shifting until the divisor is
|
||||||
|
@ larger than the dividend.
|
||||||
|
cmp divisor, #0x10000000
|
||||||
|
cmpcc divisor, dividend
|
||||||
|
movcc divisor, divisor, lsl #4
|
||||||
|
movcc curbit, curbit, lsl #4
|
||||||
|
bcc Loop1
|
||||||
|
Lbignum:
|
||||||
|
@ For very big divisors, we must shift it a bit at a time, or
|
||||||
|
@ we will be in danger of overflowing.
|
||||||
|
cmp divisor, #0x80000000
|
||||||
|
cmpcc divisor, dividend
|
||||||
|
movcc divisor, divisor, lsl #1
|
||||||
|
movcc curbit, curbit, lsl #1
|
||||||
|
bcc Lbignum
|
||||||
|
Loop3:
|
||||||
|
@ Test for possible subtractions, and note which bits
|
||||||
|
@ are done in the result. On the final pass, this may subtract
|
||||||
|
@ too much from the dividend, but the result will be ok, since the
|
||||||
|
@ "bit" will have been shifted out at the bottom.
|
||||||
|
cmp dividend, divisor
|
||||||
|
subcs dividend, dividend, divisor
|
||||||
|
orrcs result, result, curbit
|
||||||
|
cmp dividend, divisor, lsr #1
|
||||||
|
subcs dividend, dividend, divisor, lsr #1
|
||||||
|
orrcs result, result, curbit, lsr #1
|
||||||
|
cmp dividend, divisor, lsr #2
|
||||||
|
subcs dividend, dividend, divisor, lsr #2
|
||||||
|
orrcs result, result, curbit, lsr #2
|
||||||
|
cmp dividend, divisor, lsr #3
|
||||||
|
subcs dividend, dividend, divisor, lsr #3
|
||||||
|
orrcs result, result, curbit, lsr #3
|
||||||
|
cmp dividend, #0 @ Early termination?
|
||||||
|
movnes curbit, curbit, lsr #4 @ No, any more bits to do?
|
||||||
|
movne divisor, divisor, lsr #4
|
||||||
|
bne Loop3
|
||||||
|
Lgot_result:
|
||||||
|
mov r0, result
|
||||||
|
mov pc, lr
|
||||||
|
Ldiv0_uidiv:
|
||||||
|
str lr, [sp, #-4]!
|
||||||
|
#bl __div0 (PLT)
|
||||||
|
mov r0, #0 @ about as wrong as it could be
|
||||||
|
ldmia sp!, {pc}
|
||||||
|
.size __udivsi3 , . - __udivsi3
|
||||||
|
|
||||||
|
.globl __aeabi_uidivmod
|
||||||
|
__aeabi_uidivmod:
|
||||||
|
|
||||||
|
stmfd sp!, {r0, r1, ip, lr}
|
||||||
|
bl __aeabi_uidiv
|
||||||
|
ldmfd sp!, {r1, r2, ip, lr}
|
||||||
|
mul r3, r0, r2
|
||||||
|
sub r1, r1, r3
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
.globl __aeabi_idivmod
|
||||||
|
__aeabi_idivmod:
|
||||||
|
|
||||||
|
stmfd sp!, {r0, r1, ip, lr}
|
||||||
|
bl __aeabi_idiv
|
||||||
|
ldmfd sp!, {r1, r2, ip, lr}
|
||||||
|
mul r3, r0, r2
|
||||||
|
sub r1, r1, r3
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
.macro ARM_DIV_BODY dividend, divisor, result, curbit
|
||||||
|
|
||||||
|
#if __LINUX_ARM_ARCH__ >= 5
|
||||||
|
|
||||||
|
clz \curbit, \divisor
|
||||||
|
clz \result, \dividend
|
||||||
|
sub \result, \curbit, \result
|
||||||
|
mov \curbit, #1
|
||||||
|
mov \divisor, \divisor, lsl \result
|
||||||
|
mov \curbit, \curbit, lsl \result
|
||||||
|
mov \result, #0
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@ Initially shift the divisor left 3 bits if possible,
|
||||||
|
@ set curbit accordingly. This allows for curbit to be located
|
||||||
|
@ at the left end of each 4 bit nibbles in the division loop
|
||||||
|
@ to save one loop in most cases.
|
||||||
|
tst \divisor, #0xe0000000
|
||||||
|
moveq \divisor, \divisor, lsl #3
|
||||||
|
moveq \curbit, #8
|
||||||
|
movne \curbit, #1
|
||||||
|
|
||||||
|
@ Unless the divisor is very big, shift it up in multiples of
|
||||||
|
@ four bits, since this is the amount of unwinding in the main
|
||||||
|
@ division loop. Continue shifting until the divisor is
|
||||||
|
@ larger than the dividend.
|
||||||
|
1: cmp \divisor, #0x10000000
|
||||||
|
cmplo \divisor, \dividend
|
||||||
|
movlo \divisor, \divisor, lsl #4
|
||||||
|
movlo \curbit, \curbit, lsl #4
|
||||||
|
blo 1b
|
||||||
|
|
||||||
|
@ For very big divisors, we must shift it a bit at a time, or
|
||||||
|
@ we will be in danger of overflowing.
|
||||||
|
1: cmp \divisor, #0x80000000
|
||||||
|
cmplo \divisor, \dividend
|
||||||
|
movlo \divisor, \divisor, lsl #1
|
||||||
|
movlo \curbit, \curbit, lsl #1
|
||||||
|
blo 1b
|
||||||
|
|
||||||
|
mov \result, #0
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@ Division loop
|
||||||
|
1: cmp \dividend, \divisor
|
||||||
|
subhs \dividend, \dividend, \divisor
|
||||||
|
orrhs \result, \result, \curbit
|
||||||
|
cmp \dividend, \divisor, lsr #1
|
||||||
|
subhs \dividend, \dividend, \divisor, lsr #1
|
||||||
|
orrhs \result, \result, \curbit, lsr #1
|
||||||
|
cmp \dividend, \divisor, lsr #2
|
||||||
|
subhs \dividend, \dividend, \divisor, lsr #2
|
||||||
|
orrhs \result, \result, \curbit, lsr #2
|
||||||
|
cmp \dividend, \divisor, lsr #3
|
||||||
|
subhs \dividend, \dividend, \divisor, lsr #3
|
||||||
|
orrhs \result, \result, \curbit, lsr #3
|
||||||
|
cmp \dividend, #0 @ Early termination?
|
||||||
|
movnes \curbit, \curbit, lsr #4 @ No, any more bits to do?
|
||||||
|
movne \divisor, \divisor, lsr #4
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro ARM_DIV2_ORDER divisor, order
|
||||||
|
|
||||||
|
#if __LINUX_ARM_ARCH__ >= 5
|
||||||
|
|
||||||
|
clz \order, \divisor
|
||||||
|
rsb \order, \order, #31
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
cmp \divisor, #(1 << 16)
|
||||||
|
movhs \divisor, \divisor, lsr #16
|
||||||
|
movhs \order, #16
|
||||||
|
movlo \order, #0
|
||||||
|
|
||||||
|
cmp \divisor, #(1 << 8)
|
||||||
|
movhs \divisor, \divisor, lsr #8
|
||||||
|
addhs \order, \order, #8
|
||||||
|
|
||||||
|
cmp \divisor, #(1 << 4)
|
||||||
|
movhs \divisor, \divisor, lsr #4
|
||||||
|
addhs \order, \order, #4
|
||||||
|
|
||||||
|
cmp \divisor, #(1 << 2)
|
||||||
|
addhi \order, \order, #3
|
||||||
|
addls \order, \order, \divisor, lsr #1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.align 5
|
||||||
|
.globl __divsi3
|
||||||
|
.globl __aeabi_idiv
|
||||||
|
__divsi3:
|
||||||
|
__aeabi_idiv:
|
||||||
|
cmp r1, #0
|
||||||
|
eor ip, r0, r1 @ save the sign of the result.
|
||||||
|
beq Ldiv0
|
||||||
|
rsbmi r1, r1, #0 @ loops below use unsigned.
|
||||||
|
subs r2, r1, #1 @ division by 1 or -1 ?
|
||||||
|
beq 10f
|
||||||
|
movs r3, r0
|
||||||
|
rsbmi r3, r0, #0 @ positive dividend value
|
||||||
|
cmp r3, r1
|
||||||
|
bls 11f
|
||||||
|
tst r1, r2 @ divisor is power of 2 ?
|
||||||
|
beq 12f
|
||||||
|
|
||||||
|
ARM_DIV_BODY r3, r1, r0, r2
|
||||||
|
|
||||||
|
cmp ip, #0
|
||||||
|
rsbmi r0, r0, #0
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
10: teq ip, r0 @ same sign ?
|
||||||
|
rsbmi r0, r0, #0
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
11: movlo r0, #0
|
||||||
|
moveq r0, ip, asr #31
|
||||||
|
orreq r0, r0, #1
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
12: ARM_DIV2_ORDER r1, r2
|
||||||
|
|
||||||
|
cmp ip, #0
|
||||||
|
mov r0, r3, lsr r2
|
||||||
|
rsbmi r0, r0, #0
|
||||||
|
mov pc, lr
|
||||||
|
|
||||||
|
Ldiv0:
|
||||||
|
|
||||||
|
str lr, [sp, #-4]!
|
||||||
|
#bl __div0
|
||||||
|
mov r0, #0 @ About as wrong as it could be.
|
||||||
|
ldr pc, [sp], #4
|
||||||
|
|
||||||
|
|
||||||
|
.global __aeabi_uldivmod
|
||||||
|
.type __aeabi_uldivmod, function
|
||||||
|
.align 0
|
||||||
|
A_0 .req r0
|
||||||
|
A_1 .req r1
|
||||||
|
B_0 .req r2
|
||||||
|
B_1 .req r3
|
||||||
|
C_0 .req r4
|
||||||
|
C_1 .req r5
|
||||||
|
D_0 .req r6
|
||||||
|
D_1 .req r7
|
||||||
|
Q_0 .req r0
|
||||||
|
Q_1 .req r1
|
||||||
|
R_0 .req r2
|
||||||
|
R_1 .req r3
|
||||||
|
__aeabi_uldivmod:
|
||||||
|
stmfd sp!, {r4, r5, r6, r7, lr}
|
||||||
|
@ Test if B == 0
|
||||||
|
orrs ip, B_0, B_1 @ Z set -> B == 0
|
||||||
|
beq L_div_by_0
|
||||||
|
@ Test if B is power of 2: (B & (B - 1)) == 0
|
||||||
|
subs C_0, B_0, #1
|
||||||
|
sbc C_1, B_1, #0
|
||||||
|
tst C_0, B_0
|
||||||
|
tsteq B_1, C_1
|
||||||
|
beq L_pow2
|
||||||
|
@ Test if A_1 == B_1 == 0
|
||||||
|
orrs ip, A_1, B_1
|
||||||
|
beq L_div_32_32
|
||||||
|
L_div_64_64:
|
||||||
|
mov C_0, #1
|
||||||
|
mov C_1, #0
|
||||||
|
@ D_0 = clz A
|
||||||
|
teq A_1, #0
|
||||||
|
clz D_0, A_1
|
||||||
|
clzeq ip, A_0
|
||||||
|
addeq D_0, D_0, ip
|
||||||
|
@ D_1 = clz B
|
||||||
|
teq B_1, #0
|
||||||
|
clz D_1, B_1
|
||||||
|
clzeq ip, B_0
|
||||||
|
addeq D_1, D_1, ip
|
||||||
|
@ if clz B - clz A > 0
|
||||||
|
subs D_0, D_1, D_0
|
||||||
|
bls L_done_shift
|
||||||
|
@ B <<= (clz B - clz A)
|
||||||
|
subs D_1, D_0, #32
|
||||||
|
rsb ip, D_0, #32
|
||||||
|
movmi B_1, B_1, lsl D_0
|
||||||
|
orrmi B_1, B_1, B_0, lsr ip
|
||||||
|
movpl B_1, B_0, lsl D_1
|
||||||
|
mov B_0, B_0, lsl D_0
|
||||||
|
@ C = 1 << (clz B - clz A)
|
||||||
|
movmi C_1, C_1, lsl D_0
|
||||||
|
orrmi C_1, C_1, C_0, lsr ip
|
||||||
|
movpl C_1, C_0, lsl D_1
|
||||||
|
mov C_0, C_0, lsl D_0
|
||||||
|
L_done_shift:
|
||||||
|
mov D_0, #0
|
||||||
|
mov D_1, #0
|
||||||
|
@ C: current bit; D: result
|
||||||
|
L_subtract:
|
||||||
|
@ if A >= B
|
||||||
|
cmp A_1, B_1
|
||||||
|
cmpeq A_0, B_0
|
||||||
|
bcc L_update
|
||||||
|
@ A -= B
|
||||||
|
subs A_0, A_0, B_0
|
||||||
|
sbc A_1, A_1, B_1
|
||||||
|
@ D |= C
|
||||||
|
orr D_0, D_0, C_0
|
||||||
|
orr D_1, D_1, C_1
|
||||||
|
L_update:
|
||||||
|
@ if A == 0: break
|
||||||
|
orrs ip, A_1, A_0
|
||||||
|
beq L_exit
|
||||||
|
@ C >>= 1
|
||||||
|
movs C_1, C_1, lsr #1
|
||||||
|
movs C_0, C_0, rrx
|
||||||
|
@ if C == 0: break
|
||||||
|
orrs ip, C_1, C_0
|
||||||
|
beq L_exit
|
||||||
|
@ B >>= 1
|
||||||
|
movs B_1, B_1, lsr #1
|
||||||
|
mov B_0, B_0, rrx
|
||||||
|
b L_subtract
|
||||||
|
L_exit:
|
||||||
|
@ Note: A, B & Q, R are aliases
|
||||||
|
mov R_0, A_0
|
||||||
|
mov R_1, A_1
|
||||||
|
mov Q_0, D_0
|
||||||
|
mov Q_1, D_1
|
||||||
|
ldmfd sp!, {r4, r5, r6, r7, pc}
|
||||||
|
L_div_32_32:
|
||||||
|
@ Note: A_0 & r0 are aliases
|
||||||
|
@ Q_1 r1
|
||||||
|
mov r1, B_0
|
||||||
|
bl __aeabi_uidivmod
|
||||||
|
mov R_0, r1
|
||||||
|
mov R_1, #0
|
||||||
|
mov Q_1, #0
|
||||||
|
ldmfd sp!, {r4, r5, r6, r7, pc}
|
||||||
|
L_pow2:
|
||||||
|
@ Note: A, B and Q, R are aliases
|
||||||
|
@ R = A & (B - 1)
|
||||||
|
and C_0, A_0, C_0
|
||||||
|
and C_1, A_1, C_1
|
||||||
|
@ Q = A >> log2(B)
|
||||||
|
@ Note: B must not be 0 here!
|
||||||
|
clz D_0, B_0
|
||||||
|
add D_1, D_0, #1
|
||||||
|
rsbs D_0, D_0, #31
|
||||||
|
bpl L_1
|
||||||
|
clz D_0, B_1
|
||||||
|
rsb D_0, D_0, #31
|
||||||
|
mov A_0, A_1, lsr D_0
|
||||||
|
add D_0, D_0, #32
|
||||||
|
L_1:
|
||||||
|
movpl A_0, A_0, lsr D_0
|
||||||
|
orrpl A_0, A_0, A_1, lsl D_1
|
||||||
|
mov A_1, A_1, lsr D_0
|
||||||
|
@ Mov back C to R
|
||||||
|
mov R_0, C_0
|
||||||
|
mov R_1, C_1
|
||||||
|
ldmfd sp!, {r4, r5, r6, r7, pc}
|
||||||
|
L_div_by_0:
|
||||||
|
#bl __div0
|
||||||
|
@ As wrong as it could be
|
||||||
|
mov Q_0, #0
|
||||||
|
mov Q_1, #0
|
||||||
|
mov R_0, #0
|
||||||
|
mov R_1, #0
|
||||||
|
ldmfd sp!, {r4, r5, r6, r7, pc}
|
||||||
|
|
72
stage2ldr/start.S
Normal file
72
stage2ldr/start.S
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: system startup
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
.arm
|
||||||
|
|
||||||
|
.extern _main
|
||||||
|
.extern __got_start
|
||||||
|
.extern __got_end
|
||||||
|
.extern __stack_addr
|
||||||
|
.extern delay
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
.section .init
|
||||||
|
|
||||||
|
_start:
|
||||||
|
@ Get real address of _start
|
||||||
|
sub r4, pc, #8
|
||||||
|
@ Subtract offset to get the address that we were loaded at
|
||||||
|
ldr r0, =_start
|
||||||
|
sub r4, r4, r0
|
||||||
|
|
||||||
|
@ Set up a stack
|
||||||
|
ldr sp, =__stack_addr
|
||||||
|
add sp, r4
|
||||||
|
|
||||||
|
@ relocate the GOT entries
|
||||||
|
ldr r1, =__got_start
|
||||||
|
add r1, r4
|
||||||
|
ldr r2, =__got_end
|
||||||
|
add r2, r4
|
||||||
|
got_loop:
|
||||||
|
@ check for the end
|
||||||
|
cmp r1, r2
|
||||||
|
beq done_got
|
||||||
|
@ read the GOT entry
|
||||||
|
ldr r3, [r1]
|
||||||
|
@ add our base address
|
||||||
|
add r3, r4
|
||||||
|
str r3, [r1]
|
||||||
|
@ move on
|
||||||
|
add r1, r1, #4
|
||||||
|
b got_loop
|
||||||
|
|
||||||
|
done_got:
|
||||||
|
|
||||||
|
@ take the plunge
|
||||||
|
mov r0, r4
|
||||||
|
bl _main
|
||||||
|
@ _main returned! Go to whatever address it returned...
|
||||||
|
mov r1, r0
|
||||||
|
mov r0, r4
|
||||||
|
mov pc, r1
|
||||||
|
|
||||||
|
.pool
|
30
stage2ldr/start.h
Normal file
30
stage2ldr/start.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: system startup
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef __START_H__
|
||||||
|
#define __START_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
void debug_output(u8 byte);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
135
stage2ldr/string.c
Normal file
135
stage2ldr/string.c
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/* string.c -- standard C string-manipulation functions.
|
||||||
|
|
||||||
|
Copyright (C) 2008 Segher Boessenkool <segher@kernel.crashing.org>
|
||||||
|
Copyright (C) 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
|
||||||
|
Portions taken from the Public Domain C Library (PDCLib).
|
||||||
|
https://negix.net/trac/pdclib
|
||||||
|
|
||||||
|
# This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
size_t strlen(const char *s)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for (len = 0; s[len]; len++)
|
||||||
|
;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strnlen(const char *s, size_t count)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
for (len = 0; s[len] && len < count; len++)
|
||||||
|
;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memset(void *b, int c, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
((unsigned char *)b)[i] = c;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memcpy(void *dst, const void *src, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
((unsigned char *)dst)[i] = ((unsigned char *)src)[i];
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcmp(const void *s1, const void *s2, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
const unsigned char * p1 = (const unsigned char *) s1;
|
||||||
|
const unsigned char * p2 = (const unsigned char *) s2;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (p1[i] != p2[i]) return p1[i] - p2[i];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcmp(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; s1[i] && s1[i] == s2[i]; i++)
|
||||||
|
;
|
||||||
|
|
||||||
|
return s1[i] - s2[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int strncmp(const char *s1, const char *s2, size_t n)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n && s1[i] && s1[i] == s2[i]; i++)
|
||||||
|
;
|
||||||
|
if (i == n) return 0;
|
||||||
|
return s1[i] - s2[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
char * strchr(const char *s, int c)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; s[i]; i++)
|
||||||
|
if (s[i] == (char)c) return (char *)s + i;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strspn(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
while (s1[len]) {
|
||||||
|
p = s2;
|
||||||
|
while (*p) {
|
||||||
|
if (s1[len] == *p)
|
||||||
|
break;
|
||||||
|
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if (!*p)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strcspn(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
while (s1[len]) {
|
||||||
|
p = s2;
|
||||||
|
while (*p)
|
||||||
|
if (s1[len] == *p++)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
27
stage2ldr/string.h
Normal file
27
stage2ldr/string.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* string.c -- standard C string-manipulation functions.
|
||||||
|
|
||||||
|
Copyright (C) 2008 Segher Boessenkool <segher@kernel.crashing.org>
|
||||||
|
Copyright (C) 2009 Haxx Enterprises <bushing@gmail.com>
|
||||||
|
|
||||||
|
# This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||||
|
# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _STRING_H
|
||||||
|
#define _STRING_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
size_t strlen(const char *);
|
||||||
|
size_t strnlen(const char *, size_t);
|
||||||
|
void *memset(void *, int, size_t);
|
||||||
|
void *memcpy(void *, const void *, size_t);
|
||||||
|
int memcmp(const void *, const void *, size_t);
|
||||||
|
int strcmp(const char *, const char *);
|
||||||
|
int strncmp(const char *, const char *, size_t);
|
||||||
|
char *strchr(const char *, int);
|
||||||
|
size_t strspn(const char *, const char *);
|
||||||
|
size_t strcspn(const char *, const char *);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
50
stage2ldr/types.h
Normal file
50
stage2ldr/types.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: types
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef __TYPES_H__
|
||||||
|
#define __TYPES_H__
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
typedef unsigned long long u64;
|
||||||
|
|
||||||
|
typedef signed char s8;
|
||||||
|
typedef signed short s16;
|
||||||
|
typedef signed int s32;
|
||||||
|
typedef signed long long s64;
|
||||||
|
|
||||||
|
typedef volatile unsigned char vu8;
|
||||||
|
typedef volatile unsigned short vu16;
|
||||||
|
typedef volatile unsigned int vu32;
|
||||||
|
typedef volatile unsigned long long vu64;
|
||||||
|
|
||||||
|
typedef volatile signed char vs8;
|
||||||
|
typedef volatile signed short vs16;
|
||||||
|
typedef volatile signed int vs32;
|
||||||
|
typedef volatile signed long long vs64;
|
||||||
|
|
||||||
|
typedef s32 size_t;
|
||||||
|
|
||||||
|
#define NULL ((void *)0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
42
stage2ldr/utils.c
Normal file
42
stage2ldr/utils.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: random utilities
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "hollywood.h"
|
||||||
|
#include "start.h"
|
||||||
|
|
||||||
|
void udelay(u32 d)
|
||||||
|
{
|
||||||
|
// should be good to max .2% error
|
||||||
|
u32 ticks = d * 19 / 10;
|
||||||
|
|
||||||
|
write32(HW_TIMER, 0);
|
||||||
|
while(read32(HW_TIMER) < ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void panic(u8 v)
|
||||||
|
{
|
||||||
|
while(1) {
|
||||||
|
// uhh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
188
stage2ldr/utils.h
Normal file
188
stage2ldr/utils.h
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||||
|
|
||||||
|
ELF loader: random utilities
|
||||||
|
|
||||||
|
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.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, version 2.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef __UTILS_H__
|
||||||
|
#define __UTILS_H__
|
||||||
|
|
||||||
|
static inline u32 read32(u32 addr)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ("ldr\t%0, [%1]" : "=r" (data) : "r" (addr));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void write32(u32 addr, u32 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("str\t%0, [%1]" : : "r" (data), "r" (addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 set32(u32 addr, u32 set)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldr\t%0, [%1]\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstr\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (set)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 clear32(u32 addr, u32 clear)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldr\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %2\n"
|
||||||
|
"\tstr\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline u32 mask32(u32 addr, u32 clear, u32 set)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldr\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %3\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstr\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (set), "r" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 read16(u32 addr)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ("ldrh\t%0, [%1]" : "=r" (data) : "r" (addr));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void write16(u32 addr, u16 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("strh\t%0, [%1]" : : "r" (data), "r" (addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 set16(u32 addr, u16 set)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrh\t%0, [%1]\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrh\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (set)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 clear16(u32 addr, u16 clear)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrh\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %2\n"
|
||||||
|
"\tstrh\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline u16 mask16(u32 addr, u16 clear, u16 set)
|
||||||
|
{
|
||||||
|
u16 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrh\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %3\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrh\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (set), "r" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 read8(u32 addr)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
__asm__ volatile ("ldrb\t%0, [%1]" : "=r" (data) : "r" (addr));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void write8(u32 addr, u8 data)
|
||||||
|
{
|
||||||
|
__asm__ volatile ("strb\t%0, [%1]" : : "r" (data), "r" (addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 set8(u32 addr, u8 set)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrb\t%0, [%1]\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrb\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (set)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 clear8(u32 addr, u8 clear)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrb\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %2\n"
|
||||||
|
"\tstrb\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline u8 mask8(u32 addr, u8 clear, u8 set)
|
||||||
|
{
|
||||||
|
u8 data;
|
||||||
|
__asm__ volatile (
|
||||||
|
"ldrb\t%0, [%1]\n"
|
||||||
|
"\tbic\t%0, %3\n"
|
||||||
|
"\torr\t%0, %2\n"
|
||||||
|
"\tstrb\t%0, [%1]"
|
||||||
|
: "=&r" (data)
|
||||||
|
: "r" (addr), "r" (set), "r" (clear)
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void udelay(u32 d);
|
||||||
|
void panic(u8 v);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user