Initial commit

This commit is contained in:
GaryOderNichts 2022-06-06 17:05:46 +02:00
commit ad6fc39cd2
43 changed files with 4102 additions and 0 deletions

19
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Build
on: [push, pull_request]
jobs:
build-binary:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Build recovery
run: |
docker build -t recoverybuilder .
docker run --rm -v ${PWD}:/project recoverybuilder make
- uses: actions/upload-artifact@v2
with:
name: recovery_menu
path: recovery_menu

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.vscode/
build/
*.elf
*.bin
*.bin.h
*_syms.h
/recovery_menu

5
Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM devkitpro/devkitarm:20220531
ENV PATH=$DEVKITARM/bin:$PATH
WORKDIR /project

339
LICENSE Normal file
View File

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

22
Makefile Normal file
View File

@ -0,0 +1,22 @@
#-------------------------------------------------------------------------------
.SUFFIXES:
#-------------------------------------------------------------------------------
.PHONY: all clean ios_kernel ios_mcp
all: recovery_menu
@echo "\033[92mDone!\033[0m"
recovery_menu: ios_kernel
python3 build_recovery_file.py $@
ios_mcp:
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_mcp
ios_kernel: ios_mcp
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_kernel
clean:
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_kernel clean
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_mcp clean
rm -f recovery_menu

60
README.md Normal file
View File

@ -0,0 +1,60 @@
![screenshot](screenshot.png)
# Wii U Recovery Menu
A simple recovery menu running on the IOSU for unbricking, which can be booted using [udpih](https://github.com/GaryOderNichts/udpih).
## Options
### Set Coldboot Title
Allows changing the current title the console boots to.
Useful for unbricking CBHC bricks.
Possible options are:
- `Wii U Menu (JPN) - 00050010-10040000`
- `Wii U Menu (USA) - 00050010-10040100`
- `Wii U Menu (EUR) - 00050010-10040200`
### Dump Syslogs
Copies all system logs to a `logs` folder on the root of the SD Card.
### Dump OTP + SEEPROM
Dumps the OTP and SEEPROM to `otp.bin` and `seeprom.bin` on the root of the SD Card.
### Start wupserver
Starts wupserver which allows connecting to the console from a PC using [wupclient](https://gist.github.com/GaryOderNichts/409672b1bd5627b9dc506fe0f812ec9e).
### Load Network Configuration
Loads a network configuration from the SD, and temporarily applies it to use wupserver.
The configurations will be loaded from a `network.cfg` file on the root of your SD.
For using the ethernet adapter, the file should look like this:
```
type=eth
```
For using wifi:
```
type=wifi
ssid=ssidhere
key=wifikeyhere
key_type=WPA2_PSK_AES
```
### Displays DRC Pin
Displays the Gamepad Pin used for pairing the gamepad.
The numeric values represent the following symbols: `♠ = 0, ♥ = 1, ♦ = 2, ♣ = 3`.
### Install WUP
Installs a valid signed WUP from the `install` folder on the root of your SD Card.
Don't place the WUP into any subfolders.
## Building
```bash
# build the docker container
docker build -t recoverybuilder .
# build the menu
docker run -it --rm -v ${PWD}:/project recoverybuilder make
```
## Credits
- [@Maschell](https://github.com/Maschell) for the [network configuration types](https://github.com/devkitPro/wut/commit/159f578b34401cd4365efd7b54b536154c9dc576)
- [@dimok789](https://github.com/dimok789) for [mocha](https://github.com/dimok789/mocha)
- [@hexkyz](https://github.com/hexkyz) for [hexFW](https://github.com/hexkyz/hexFW)
- [@rw-r-r-0644](https://github.com/rw-r-r-0644) for the lolserial code

51
build_recovery_file.py Normal file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python3
import sys, struct
RECOVERY_HEADER_SIZE = 0xec
def defSection(vaddr, paddr, size, offset):
return struct.pack('>IIII', vaddr, paddr, size, offset)
def main(argc, argv):
if argc != 2:
sys.exit(-1)
# magic
data = b'REC\0'
# entrypoint and numSections
data += struct.pack('>II', 0x08136000, 2)
# read kernel binary
kernel_bin = b''
with open("ios_kernel/ios_kernel.bin", "rb") as f:
kernel_bin += f.read()
# kernel section
data += defSection(0x08136000, 0x08136000, len(kernel_bin), RECOVERY_HEADER_SIZE)
# read mcp binary
mcp_bin = b''
with open("ios_mcp/ios_mcp.bin", "rb") as f:
mcp_bin += f.read()
# mcp section
data += defSection(0x05116000, 0x05116000 - 0x05100000 + 0x13d80000, len(mcp_bin), RECOVERY_HEADER_SIZE + len(kernel_bin))
# pad the rest of the section structs
for i in range(12):
data += defSection(0, 0, 0, 0)
# append kernel binary
data += kernel_bin
# append mcp binary
data += mcp_bin
# write file
with open(argv[1], 'wb') as f:
f.write(data)
if __name__ == '__main__':
main(len(sys.argv), sys.argv)

135
ios_kernel/Makefile Normal file
View File

@ -0,0 +1,135 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
#---------------------------------------------------------------------------------
# iosu_rules
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>/devkitARM")
endif
include $(DEVKITARM)/base_rules
export OBJDUMP := $(PREFIX)objdump
MACHDEP = -DSTARBUCK -mbig-endian -mcpu=arm926ej-s -msoft-float -mfloat-abi=soft
%.elf:
@echo linking ... $(notdir $@)
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# 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
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := source
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS := -Wall -std=gnu11 -Os -flto $(MACHDEP) $(INCLUDE) -Wno-stringop-overflow
ASFLAGS := $(MACHDEP)
LDFLAGS := -nostartfiles -nodefaultlibs -mbig-endian -flto \
-Wl,-L $(TOPDIR) -Wl,-Map,$(notdir $*.map),-T $(TOPDIR)/link.ld
LIBS := -lgcc
#---------------------------------------------------------------------------------
# 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 TARGETNAME := $(TARGET)
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export LD := $(CC)
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(SFILES:.s=.o) $(CFILES:.c=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
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) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).bin
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).bin
$(OUTPUT).elf : $(OFILES)
$(OUTPUT).bin: $(OUTPUT).elf
@echo "built ... $(notdir $@)"
@$(OBJCOPY) -j .text -j .rodata -j .data -O binary $(OUTPUT).elf $@
$(OFILES_SRC) : $(HFILES_BIN)
#-------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#-------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#-------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

32
ios_kernel/imports.ld Normal file
View File

@ -0,0 +1,32 @@
PROVIDE(memcpy = 0x08131d04);
PROVIDE(memset = 0x08131da0);
PROVIDE(strncpy = 0x081329b8);
PROVIDE(vsnprintf = 0x0813293c);
PROVIDE(snprintf = 0x08132988);
PROVIDE(disable_interrupts = 0x0812e778);
PROVIDE(enable_interrupts = 0x0812e78c);
PROVIDE(invalidate_icache = 0x0812dcf0);
PROVIDE(invalidate_dcache = 0x08120164);
PROVIDE(flush_dcache = 0x08120160);
PROVIDE(setClientCapabilities = 0x081260a8);
PROVIDE(_iosMapSharedUserExecution = 0x08124f88);
PROVIDE(readOTP = 0x08120248);
PROVIDE(IOS_HeapAlloc = 0x081234e4);
PROVIDE(IOS_HeapAllocAligned = 0x08123464);
PROVIDE(IOS_HeapFree = 0x08123830);
PROVIDE(IOS_Open = 0x0812940c);
PROVIDE(IOS_Close = 0x08129368);
PROVIDE(IOS_Ioctl = 0x081290e0);
PROVIDE(IOS_Ioctlv = 0x0812903c);
PROVIDE(IOS_VirtToPhys = 0x08124484);
PROVIDE(IOS_Shutdown = 0xffffdc48);
PROVIDE(IOS_Reset = 0x08129760);
PROVIDE(currentThreadContext = 0x08173ba0);
PROVIDE(domainAccessPermissions = 0x081a4000);

29
ios_kernel/link.ld Normal file
View File

@ -0,0 +1,29 @@
OUTPUT_ARCH(arm)
INCLUDE "imports.ld"
SECTIONS {
. = (0x08136000);
.text : {
crt0.o(.init)
*(.text*);
}
.rodata : {
*(.rodata*)
}
.data : {
*(.data*)
}
.bss : {
__kernel_bss_start = .;
*(.bss*)
}
__kernel_bss_end = .;
/DISCARD/ : {
*(*);
}
}
ASSERT((SIZEOF(.text) + SIZEOF(.rodata) + SIZEOF(.data) + SIZEOF(.bss)) < 0xa000, "ios_kernel is too big");

9
ios_kernel/source/crt0.s Normal file
View File

@ -0,0 +1,9 @@
.section ".init"
.arm
.align 4
.extern _main
.type _main, %function
_start:
b _main

View File

@ -0,0 +1,69 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#define ARM_B(addr, func) (0xEA000000 | ((((uint32_t)(func) - (uint32_t)(addr) - 8) >> 2) & 0x00FFFFFF)) // +-32MB
#define ARM_BL(addr, func) (0xEB000000 | ((((uint32_t)(func) - (uint32_t)(addr) - 8) >> 2) & 0x00FFFFFF)) // +-32MB
#define THUMB_B(addr, func) ((0xE000 | ((((uint32_t)(func) - (uint32_t)(addr) - 4) >> 1) & 0x7FF))) // +-2KB
#define THUMB_BL(addr, func) ((0xF000F800 | ((((uint32_t)(func) - (uint32_t)(addr) - 4) >> 1) & 0x0FFF)) | ((((uint32_t)(func) - (uint32_t)(addr) - 4) << 4) & 0x7FFF000)) // +-4MB
typedef struct {
void* ptr;
uint32_t len;
uint32_t paddr;
} IOSVec_t;
typedef struct {
uint32_t paddr;
uint32_t vaddr;
uint32_t size;
uint32_t domain;
// 0 = undefined, 1 = kernel only, 2 = read only, 3 = read write
uint32_t type;
uint32_t cached;
} ios_map_shared_info_t;
int disable_interrupts(void);
int enable_interrupts(int);
void invalidate_icache(void);
void invalidate_dcache(void* ptr, uint32_t size);
void flush_dcache(void* ptr, uint32_t size);
int setClientCapabilities(int processId, int featureId, uint64_t featureMask);
int _iosMapSharedUserExecution(ios_map_shared_info_t* info);
int readOTP(int index, void* buf, uint32_t size);
void* IOS_HeapAlloc(uint32_t heap, uint32_t size);
void* IOS_HeapAllocAligned(uint32_t heap, uint32_t size, uint32_t alignment);
void IOS_HeapFree(uint32_t heap, void* ptr);
int IOS_Open(const char* device, int mode);
int IOS_Close(int fd);
int IOS_Ioctl(int fd, uint32_t request, void* input_buffer, uint32_t input_buffer_len, void* output_buffer, uint32_t output_buffer_len);
int IOS_Ioctlv(int fd, uint32_t request, uint32_t vector_count_in, uint32_t vector_count_out, IOSVec_t* vector);
void* IOS_VirtToPhys(void* ptr);
void IOS_Shutdown(int reset);
void IOS_Reset(void);
static inline uint32_t disable_mmu(void)
{
uint32_t control_register = 0;
asm volatile("MRC p15, 0, %0, c1, c0, 0" : "=r" (control_register));
asm volatile("MCR p15, 0, %0, c1, c0, 0" : : "r" (control_register & 0xFFFFEFFA));
return control_register;
}
static inline void restore_mmu(uint32_t control_register)
{
asm volatile("MCR p15, 0, %0, c1, c0, 0" : : "r" (control_register));
}
static inline void set_domain_register(uint32_t domain_register)
{
asm volatile("MCR p15, 0, %0, c3, c0, 0" : : "r"(domain_register));
}

View File

@ -0,0 +1,64 @@
#include "lolserial.h"
#include "imports.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#define LT_REG_BASE (0x0d800000)
#define LT_TIMER (LT_REG_BASE + 0x010)
#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_OWNER (LT_REG_BASE + 0x0fc)
#define LOLSERIAL_WAIT_TICKS 200
#if 0
#define LOLSERIAL_PIN 0x00000100 //GP_SENSORBAR
#else
#define LOLSERIAL_PIN 0x00010000 // GP_DEBUG0
#endif
void lolserial_lprint(const char *str, int len)
{
/* setup output pin */
*(volatile uint32_t*) LT_GPIO_OWNER &= ~LOLSERIAL_PIN;
*(volatile uint32_t*) LT_GPIO_ENABLE |= LOLSERIAL_PIN;
*(volatile uint32_t*) LT_GPIO_DIR |= LOLSERIAL_PIN;
*(volatile uint32_t*) LT_GPIO_OUT |= LOLSERIAL_PIN;
/* loop until null terminator or string end */
for (const char *end = str + len; *str && (str != end); str++) {
for (uint32_t bits = 0x200 | (*str << 1); bits; bits >>= 1) {
/* set bit value */
*(volatile uint32_t*) LT_GPIO_OUT = ((*(volatile uint32_t*) LT_GPIO_OUT) & ~LOLSERIAL_PIN) | ((bits & 1) ? LOLSERIAL_PIN : 0);
/* wait ticks for bit */
uint32_t now = *(volatile uint32_t*) LT_TIMER, then = now + LOLSERIAL_WAIT_TICKS;
for (; then < now; now = *(volatile uint32_t*) LT_TIMER); /* wait overflow */
for (; now < then; now = *(volatile uint32_t*) LT_TIMER); /* wait */
}
}
}
void lolserial_print(const char *str)
{
lolserial_lprint(str, -1);
}
void lolserial_printf(const char* format, ...)
{
int level = disable_interrupts();
va_list args;
va_start(args, format);
char buffer[0x100];
int len = vsnprintf(buffer, sizeof(buffer), format, args);
lolserial_lprint(buffer, len);
va_end(args);
enable_interrupts(level);
}

View File

@ -0,0 +1,7 @@
#pragma once
void lolserial_lprint(const char *str, int len);
void lolserial_print(const char *str);
void lolserial_printf(const char* fmt, ...);

94
ios_kernel/source/main.c Normal file
View File

@ -0,0 +1,94 @@
#include "imports.h"
#include "thread.h"
#include "lolserial.h"
#include "../../ios_mcp/ios_mcp_syms.h"
extern char __kernel_bss_start;
extern char __kernel_bss_end;
extern char svcAB_handler;
extern uint32_t domainAccessPermissions[];
int kernel_syscall_0x81(int type, uint32_t address, uint32_t value)
{
int res = 0;
int level = disable_interrupts();
set_domain_register(domainAccessPermissions[0]); // 0 = KERNEL
if (type == 0) { // kernRead32
res = *(volatile uint32_t*) address;
} else if (type == 1) { // kernWrite32
*(volatile uint32_t*) address = value;
} else if (type == 2) { // readOtp
res = readOTP(0, (void*) address, value);
}
set_domain_register(domainAccessPermissions[currentThreadContext->pid]);
enable_interrupts(level);
return res;
}
int _main(void* arg)
{
lolserial_printf("Hello world from recovery_menu. Running from '%s'.\n", arg);
int level = disable_interrupts();
uint32_t control_register = disable_mmu();
// clear all bss
memset(&__kernel_bss_start, 0, &__kernel_bss_end - &__kernel_bss_start);
memset((void*) (__mcp_bss_start - 0x05074000 + 0x08234000), 0, __mcp_bss_end - __mcp_bss_start);
// map the mcp sections
ios_map_shared_info_t map_info;
map_info.paddr = 0x050bd000 - 0x05000000 + 0x081c0000;
map_info.vaddr = 0x050bd000;
map_info.size = 0x3000;
map_info.domain = 1; // MCP
map_info.type = 3;
map_info.cached = 0xffffffff;
_iosMapSharedUserExecution(&map_info);
map_info.paddr = 0x05116000 - 0x05100000 + 0x13d80000;
map_info.vaddr = 0x05116000;
map_info.size = 0x4000;
map_info.domain = 1; // MCP
map_info.type = 3;
map_info.cached = 0xffffffff;
_iosMapSharedUserExecution(&map_info);
// redirect __sys_write0 to lolserial
*(volatile uint32_t*) 0x0812dd68 = ARM_B(0x0812dd68, (uint32_t) &svcAB_handler);
// add mcp ioctl hook to start mcp thread
*(volatile uint32_t*) (0x05025242 - 0x05000000 + 0x081c0000) = THUMB_BL(0x05025242, _MCP_ioctl100_patch);
// replace custom kernel syscall
*(volatile uint32_t*) 0x0812cd2c = ARM_B(0x0812cd2c, kernel_syscall_0x81);
restore_mmu(control_register);
// invalidate all cache
invalidate_dcache(NULL, 0x4001);
invalidate_icache();
enable_interrupts(level);
// give the current thread full access to MCP for starting the thread
setClientCapabilities(currentThreadContext->pid, 0xd, 0xffffffffffffffffllu);
// start mcp thread
int mcpHandle = IOS_Open("/dev/mcp", 0);
if (mcpHandle > 0) {
lolserial_printf("Starting MCP thread...\n");
IOS_Ioctl(mcpHandle, 100, NULL, 0, NULL, 0);
IOS_Close(mcpHandle);
} else {
lolserial_printf("Cannot open MCP: %x\n", mcpHandle);
}
return 0;
}

View File

@ -0,0 +1,11 @@
.arm
.extern lolserial_print
.global svcAB_handler
svcAB_handler:
ldmia sp, {r0, r1}
cmp r0, #0x4
mov r0, r1
bleq lolserial_print
ldmia sp!, {r0-r12, pc}^

View File

@ -0,0 +1,33 @@
#pragma once
#include <assert.h>
#include <stdint.h>
typedef struct ThreadContext {
uint32_t cspr;
uint32_t gpr[14];
uint32_t lr;
uint32_t pc;
struct ThreadContext* threadQueueNext;
uint32_t maxPriority;
uint32_t priority;
uint32_t state;
uint32_t pid;
uint32_t id;
uint32_t flags;
uint32_t exitValue;
struct ThreadContext** joinQueue;
struct ThreadContext** threadQueue;
uint8_t unk1[0x38];
void* stackPointer;
uint8_t unk2[8];
void* sysStackAddr;
void* userStackAddr;
uint32_t userStackSize;
void* threadLocalStorage;
uint32_t profileCount;
uint32_t profileTime;
} ThreadContext_t;
static_assert(sizeof(ThreadContext_t) == 0xc8, "ThreadContext_t: different size than expected");
extern ThreadContext_t* currentThreadContext;

142
ios_mcp/Makefile Normal file
View File

@ -0,0 +1,142 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
#---------------------------------------------------------------------------------
# iosu_rules
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>/devkitARM")
endif
include $(DEVKITARM)/base_rules
export OBJDUMP := $(PREFIX)objdump
MACHDEP = -DSTARBUCK -mbig-endian -mcpu=arm926ej-s -msoft-float -mfloat-abi=soft
%.elf:
@echo linking ... $(notdir $@)
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# 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
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := source
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS := -Wall -std=gnu11 -Os -flto -fno-tree-loop-distribute-patterns -fno-builtin $(MACHDEP) $(INCLUDE)
ASFLAGS := $(MACHDEP)
LDFLAGS := -nostartfiles -nodefaultlibs -mbig-endian -flto \
-Wl,-L $(TOPDIR) -Wl,-Map,$(notdir $*.map),-T $(TOPDIR)/link.ld
LIBS := -lgcc
#---------------------------------------------------------------------------------
# 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 TARGETNAME := $(TARGET)
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export LD := $(CC)
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(SFILES:.s=.o) $(CFILES:.c=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
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) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).bin $(TARGET)_syms.h
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).bin $(OUTPUT)_syms.h
$(OUTPUT).elf : $(OFILES)
$(OUTPUT).bin: $(OUTPUT).elf
@echo "built ... $(notdir $@)"
@$(OBJCOPY) -j .text -j .rodata -j .data -O binary $(OUTPUT).elf $@
$(OUTPUT)_syms.h:
@echo "#ifndef $(TARGETNAME)_SYMS_H" > $@
@echo "#define $(TARGETNAME)_SYMS_H" >> $@
@$(OBJDUMP) -EB -t -marm $(OUTPUT).elf | grep 'g F .text' | grep -v '.hidden' | awk '{print "#define " $$6 " 0x" $$1}' >> $@
@$(OBJDUMP) -EB -t -marm $(OUTPUT).elf | grep -e 'g .text' -e '_bss_' | awk '{print "#define " $$5 " 0x" $$1}' >> $@
@echo "#endif" >> $@
$(OFILES_SRC) : $(HFILES_BIN)
#-------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#-------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#-------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

44
ios_mcp/imports.ld Normal file
View File

@ -0,0 +1,44 @@
/* PROVIDE(printf = 0x0503dcc0); */
PROVIDE(printf = 0x05055438);
PROVIDE(memcmp = 0x05054d6c);
PROVIDE(memcpy = 0x05054e54);
PROVIDE(memset = 0x05054ef0);
PROVIDE(vsnprintf = 0x05055c40);
PROVIDE(snprintf = 0x05055c8c);
PROVIDE(strncpy = 0x05055db4);
PROVIDE(strnlen = 0x05055e94);
PROVIDE(strncmp = 0x05055e10);
PROVIDE(usleep = 0x050564e4);
PROVIDE(bspWrite = 0x0503d460);
PROVIDE(bspRead = 0x0503d550);
PROVIDE(IOS_CreateThread = 0x050567ec);
PROVIDE(IOS_JoinThread = 0x050567f4);
PROVIDE(IOS_CancelThread = 0x050567fc);
PROVIDE(IOS_StartThread = 0x05056824);
PROVIDE(IOS_GetThreadPriority = 0x0505683c);
PROVIDE(IOS_CreateMessageQueue = 0x0505684c);
PROVIDE(IOS_DestroyMessageQueue = 0x05056854);
PROVIDE(IOS_ReceiveMessage = 0x0505686c);
PROVIDE(IOS_HeapAlloc = 0x05056924);
PROVIDE(IOS_HeapAllocAligned = 0x0505692c);
PROVIDE(IOS_HeapFree = 0x05056934);
PROVIDE(IOS_Open = 0x05056984);
PROVIDE(IOS_Close = 0x0505698c);
PROVIDE(IOS_Ioctl = 0x050569ac);
PROVIDE(IOS_Ioctlv = 0x050569b4);
PROVIDE(IOS_IoctlvAsync = 0x050569ec);
PROVIDE(IOS_InvalidateDCache = 0x05056a74);
PROVIDE(IOS_FlushDCache = 0x05056a7c);
PROVIDE(IOS_Shutdown = 0x05056b7c);
PROVIDE(IOS_Syscall0x81 = 0x05056bf4);
PROVIDE(ppcHeartBeatThreadId = 0x05070458);
PROVIDE(currentColdbootOS = 0x050b8174);
PROVIDE(currentColdbootTitle = 0x050b817c);

24
ios_mcp/link.ld Normal file
View File

@ -0,0 +1,24 @@
OUTPUT_ARCH(arm)
INCLUDE "imports.ld"
SECTIONS {
.text 0x05116000 : {
*(.text*);
*(.rodata*);
*(.data*);
}
.bss 0x050bd000 : {
__mcp_bss_start = .;
*(.bss*);
}
__mcp_bss_end = .;
/DISCARD/ : {
*(*);
}
}
ASSERT(SIZEOF(.text) < 0x4000, "ios_mcp text is too big");
ASSERT(SIZEOF(.bss) < 0x3000, "ios_mcp bss is too big");

54
ios_mcp/source/ccr.c Normal file
View File

@ -0,0 +1,54 @@
#include "ccr.h"
#include "imports.h"
#include <string.h>
static void* allocIobuf(uint32_t size)
{
void* ptr = IOS_HeapAlloc(0xcaff, size);
memset(ptr, 0x00, size);
return ptr;
}
static void freeIobuf(void* ptr)
{
IOS_HeapFree(0xcaff, ptr);
}
int CCRCDCGetMacAddress(int handle, uint8_t destId, void* macAddress)
{
uint8_t* buf = allocIobuf(0x128 + sizeof(IOSVec_t) * 2);
IOSVec_t* vecs = (IOSVec_t*) (buf + 0x128);
buf[0x17] = destId;
vecs[0].ptr = buf;
vecs[0].len = 0x128;
vecs[1].ptr = buf;
vecs[1].len = 0x128;
int res = IOS_Ioctlv(handle, 0xc9, 1, 1, vecs);
if (res >= 0) {
memcpy(macAddress, buf + 0x18, 7);
}
freeIobuf(buf);
return res;
}
int CCRSysGetPincode(int handle, uint8_t* pin)
{
uint8_t mac[7];
int res = CCRCDCGetMacAddress(handle, 1, mac);
if (res >= 0) {
pin[3] = mac[6] & 3;
pin[2] = (mac[6] >> 2) & 3;
pin[1] = (mac[6] >> 4) & 3;
pin[0] = mac[6] >> 6;
return 0;
}
return res;
}

7
ios_mcp/source/ccr.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
int CCRCDCGetMacAddress(int handle, uint8_t destId, void* macAddress);
int CCRSysGetPincode(int handle, uint8_t* pin);

65
ios_mcp/source/font_bin.h Normal file
View File

@ -0,0 +1,65 @@
static const unsigned char font_bin[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x18,
0x18, 0x00, 0x0c, 0x00, 0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00,
0x00, 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00, 0x18, 0x7c, 0x06,
0x3c, 0x60, 0x3e, 0x18, 0x10, 0x46, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x62,
0x00, 0x3c, 0x66, 0x3c, 0x1c, 0xe6, 0x66, 0xfc, 0x00, 0x18, 0x0c, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x18, 0x30, 0x00,
0x00, 0x0c, 0x18, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x66, 0x3c, 0xff,
0x3c, 0x66, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x3c, 0x66, 0x76,
0x6e, 0x66, 0x3c, 0x00, 0x00, 0x18, 0x1c, 0x18, 0x18, 0x18, 0x7e, 0x00,
0x00, 0x3c, 0x62, 0x30, 0x0c, 0x06, 0x7e, 0x00, 0x00, 0x3c, 0x62, 0x38,
0x60, 0x66, 0x3c, 0x00, 0x00, 0x6c, 0x6c, 0x66, 0xfe, 0x60, 0x60, 0x00,
0x00, 0x7e, 0x06, 0x7e, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x3c, 0x06, 0x3e,
0x66, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00,
0x00, 0x3c, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x3c, 0x66, 0x7c,
0x60, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00,
0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x70, 0x1c, 0x06,
0x06, 0x1c, 0x70, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x0e, 0x38, 0x60, 0x60, 0x38, 0x0e, 0x00, 0x00, 0x3c, 0x66, 0x30,
0x18, 0x00, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3c,
0x00, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x3e, 0x66, 0x3e,
0x66, 0x66, 0x3e, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x66, 0x3c, 0x00,
0x00, 0x1e, 0x36, 0x66, 0x66, 0x36, 0x1e, 0x00, 0x00, 0x7e, 0x06, 0x1e,
0x06, 0x06, 0x7e, 0x00, 0x00, 0x3e, 0x06, 0x1e, 0x06, 0x06, 0x06, 0x00,
0x00, 0x3c, 0x66, 0x06, 0x76, 0x66, 0x3c, 0x00, 0x00, 0x66, 0x66, 0x7e,
0x66, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x66, 0x36, 0x1e,
0x1e, 0x36, 0x66, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7e, 0x00,
0x00, 0x46, 0x6e, 0x7e, 0x56, 0x46, 0x46, 0x00, 0x00, 0x66, 0x6e, 0x7e,
0x76, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
0x00, 0x3e, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, 0x3c, 0x66, 0x66,
0x66, 0x3c, 0x70, 0x00, 0x00, 0x3e, 0x66, 0x3e, 0x1e, 0x36, 0x66, 0x00,
0x00, 0x3c, 0x66, 0x0c, 0x30, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x18, 0x18,
0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x46, 0x46, 0x56,
0x7e, 0x6e, 0x46, 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00,
0x00, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x30, 0x18,
0x0c, 0x06, 0x7e, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c,
0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x3c, 0x30, 0x30,
0x30, 0x30, 0x30, 0x3c, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x0c, 0x18, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x7c, 0x66, 0x7c, 0x00,
0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x3c, 0x06,
0x06, 0x06, 0x3c, 0x00, 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
0x00, 0x00, 0x3c, 0x66, 0x7e, 0x06, 0x3c, 0x00, 0x00, 0x38, 0x0c, 0x3e,
0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x7c, 0x40, 0x3c, 0x00,
0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x18, 0x00, 0x1c,
0x18, 0x18, 0x3c, 0x00, 0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1e, 0x00,
0x00, 0x06, 0x06, 0x36, 0x1e, 0x36, 0x66, 0x00, 0x00, 0x1c, 0x18, 0x18,
0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x66, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3c, 0x66,
0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x00,
0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x00, 0x00, 0x00, 0x3e, 0x66,
0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x3c, 0x60, 0x3e, 0x00,
0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x66, 0x66,
0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
0x00, 0x00, 0xc6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, 0x66, 0x3c,
0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66, 0x7c, 0x60, 0x3c, 0x00,
0x00, 0x00, 0x7e, 0x30, 0x18, 0x0c, 0x7e, 0x00, 0x00, 0x00, 0x18, 0x08,
0x08, 0x04, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x0c, 0x08, 0x08, 0x10, 0x08, 0x08
};

450
ios_mcp/source/fsa.c Normal file
View File

@ -0,0 +1,450 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "imports.h"
#include "fsa.h"
static void* allocIobuf()
{
void* ptr = IOS_HeapAlloc(0xcaff, 0x828);
memset(ptr, 0x00, 0x828);
return ptr;
}
static void freeIobuf(void* ptr)
{
IOS_HeapFree(0xcaff, ptr);
}
int FSA_Mount(int fd, const char* device_path, char* volume_path, uint32_t flags, char* arg_string, int arg_string_len)
{
uint8_t* iobuf = allocIobuf();
uint8_t* inbuf8 = iobuf;
uint8_t* outbuf8 = &iobuf[0x520];
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
uint32_t* inbuf = (uint32_t*)inbuf8;
uint32_t* outbuf = (uint32_t*)outbuf8;
strncpy((char*)&inbuf8[0x04], device_path, 0x27F);
strncpy((char*)&inbuf8[0x284], volume_path, 0x27F);
inbuf[0x504 / 4] = (uint32_t)flags;
inbuf[0x508 / 4] = (uint32_t)arg_string_len;
iovec[0].ptr = inbuf;
iovec[0].len = 0x520;
iovec[1].ptr = arg_string;
iovec[1].len = arg_string_len;
iovec[2].ptr = outbuf;
iovec[2].len = 0x293;
int ret = IOS_Ioctlv(fd, 0x01, 2, 1, iovec);
freeIobuf(iobuf);
return ret;
}
int FSA_Unmount(int fd, const char* path, uint32_t flags)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
inbuf[0x284 / 4] = flags;
int ret = IOS_Ioctl(fd, 0x02, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_FlushVolume(int fd, const char* volume_path)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], volume_path, 0x27F);
int ret = IOS_Ioctl(fd, 0x1B, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_MakeDir(int fd, const char* path, uint32_t flags)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
inbuf[0x284 / 4] = flags;
int ret = IOS_Ioctl(fd, 0x07, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_OpenDir(int fd, const char* path, int* outHandle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
int ret = IOS_Ioctl(fd, 0x0A, inbuf, 0x520, outbuf, 0x293);
if(outHandle) *outHandle = outbuf[1];
freeIobuf(iobuf);
return ret;
}
int FSA_ReadDir(int fd, int handle, FSDirectoryEntry* out_data)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = handle;
int ret = IOS_Ioctl(fd, 0x0B, inbuf, 0x520, outbuf, 0x293);
if(out_data) memcpy(out_data, &outbuf[1], sizeof(FSDirectoryEntry));
freeIobuf(iobuf);
return ret;
}
int FSA_RewindDir(int fd, int handle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = handle;
int ret = IOS_Ioctl(fd, 0x0C, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_CloseDir(int fd, int handle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = handle;
int ret = IOS_Ioctl(fd, 0x0D, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_ChangeDir(int fd, const char* path)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
int ret = IOS_Ioctl(fd, 0x05, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_OpenFile(int fd, const char* path, const char* mode, int* outHandle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
strncpy((char*)&inbuf[0xA1], mode, 0x10);
int ret = IOS_Ioctl(fd, 0x0E, inbuf, 0x520, outbuf, 0x293);
if(outHandle) *outHandle = outbuf[1];
freeIobuf(iobuf);
return ret;
}
int _FSA_ReadWriteFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags, int read)
{
uint8_t* iobuf = allocIobuf();
uint8_t* inbuf8 = iobuf;
uint8_t* outbuf8 = &iobuf[0x520];
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
uint32_t* inbuf = (uint32_t*)inbuf8;
uint32_t* outbuf = (uint32_t*)outbuf8;
inbuf[0x08 / 4] = size;
inbuf[0x0C / 4] = cnt;
inbuf[0x14 / 4] = fileHandle;
inbuf[0x18 / 4] = flags;
iovec[0].ptr = inbuf;
iovec[0].len = 0x520;
iovec[1].ptr = data;
iovec[1].len = size * cnt;
iovec[2].ptr = outbuf;
iovec[2].len = 0x293;
int ret;
if(read) ret = IOS_Ioctlv(fd, 0x0F, 1, 2, iovec);
else ret = IOS_Ioctlv(fd, 0x10, 2, 1, iovec);
freeIobuf(iobuf);
return ret;
}
int FSA_ReadFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags)
{
return _FSA_ReadWriteFile(fd, data, size, cnt, fileHandle, flags, 1);
}
int FSA_WriteFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags)
{
return _FSA_ReadWriteFile(fd, data, size, cnt, fileHandle, flags, 0);
}
int FSA_StatFile(int fd, int handle, FSStat* out_data)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = handle;
int ret = IOS_Ioctl(fd, 0x14, inbuf, 0x520, outbuf, 0x293);
if(out_data) memcpy(out_data, &outbuf[1], sizeof(FSStat));
freeIobuf(iobuf);
return ret;
}
int FSA_CloseFile(int fd, int fileHandle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = fileHandle;
int ret = IOS_Ioctl(fd, 0x15, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_SetPosFile(int fd, int fileHandle, uint32_t position)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = fileHandle;
inbuf[2] = position;
int ret = IOS_Ioctl(fd, 0x12, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_GetStat(int fd, const char *path, FSStat* out_data)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
inbuf[0x284/4] = 5;
int ret = IOS_Ioctl(fd, 0x18, inbuf, 0x520, outbuf, 0x293);
if(out_data) memcpy(out_data, &outbuf[1], sizeof(FSStat));
freeIobuf(iobuf);
return ret;
}
int FSA_Remove(int fd, const char *path)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
int ret = IOS_Ioctl(fd, 0x08, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
int FSA_ChangeMode(int fd, const char *path, int mode)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], path, 0x27F);
inbuf[0x284/4] = mode;
inbuf[0x288/4] = 0x777; // mask
int ret = IOS_Ioctl(fd, 0x20, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
// type 4 :
// 0x08 : device size in sectors (uint64_t)
// 0x10 : device sector size (uint32_t)
int FSA_GetDeviceInfo(int fd, const char* device_path, int type, uint32_t* out_data)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], device_path, 0x27F);
inbuf[0x284 / 4] = type;
int ret = IOS_Ioctl(fd, 0x18, inbuf, 0x520, outbuf, 0x293);
int size = 0;
switch(type)
{
case 0: case 1: case 7:
size = 0x8;
break;
case 2:
size = 0x4;
break;
case 3:
size = 0x1E;
break;
case 4:
size = 0x28;
break;
case 5:
size = 0x64;
break;
case 6: case 8:
size = 0x14;
break;
}
memcpy(out_data, &outbuf[1], size);
freeIobuf(iobuf);
return ret;
}
int FSA_RawOpen(int fd, const char* device_path, int* outHandle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
strncpy((char*)&inbuf[0x01], device_path, 0x27F);
int ret = IOS_Ioctl(fd, 0x6A, inbuf, 0x520, outbuf, 0x293);
if(outHandle) *outHandle = outbuf[1];
freeIobuf(iobuf);
return ret;
}
int FSA_RawClose(int fd, int device_handle)
{
uint8_t* iobuf = allocIobuf();
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
inbuf[1] = device_handle;
int ret = IOS_Ioctl(fd, 0x6D, inbuf, 0x520, outbuf, 0x293);
freeIobuf(iobuf);
return ret;
}
// offset in blocks of 0x1000 bytes
int FSA_RawRead(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle)
{
uint8_t* iobuf = allocIobuf();
uint8_t* inbuf8 = iobuf;
uint8_t* outbuf8 = &iobuf[0x520];
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
uint32_t* inbuf = (uint32_t*)inbuf8;
uint32_t* outbuf = (uint32_t*)outbuf8;
// note : offset_bytes = blocks_offset * size_bytes
inbuf[0x08 / 4] = (blocks_offset >> 32);
inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF);
inbuf[0x10 / 4] = cnt;
inbuf[0x14 / 4] = size_bytes;
inbuf[0x18 / 4] = device_handle;
iovec[0].ptr = inbuf;
iovec[0].len = 0x520;
iovec[1].ptr = data;
iovec[1].len = size_bytes * cnt;
iovec[2].ptr = outbuf;
iovec[2].len = 0x293;
int ret = IOS_Ioctlv(fd, 0x6B, 1, 2, iovec);
freeIobuf(iobuf);
return ret;
}
int FSA_RawWrite(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle)
{
uint8_t* iobuf = allocIobuf();
uint8_t* inbuf8 = iobuf;
uint8_t* outbuf8 = &iobuf[0x520];
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
uint32_t* inbuf = (uint32_t*)inbuf8;
uint32_t* outbuf = (uint32_t*)outbuf8;
inbuf[0x08 / 4] = (blocks_offset >> 32);
inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF);
inbuf[0x10 / 4] = cnt;
inbuf[0x14 / 4] = size_bytes;
inbuf[0x18 / 4] = device_handle;
iovec[0].ptr = inbuf;
iovec[0].len = 0x520;
iovec[1].ptr = data;
iovec[1].len = size_bytes * cnt;
iovec[2].ptr = outbuf;
iovec[2].len = 0x293;
int ret = IOS_Ioctlv(fd, 0x6C, 2, 1, iovec);
freeIobuf(iobuf);
return ret;
}

54
ios_mcp/source/fsa.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include "imports.h"
typedef struct {
uint32_t flags;
uint32_t mode;
uint32_t owner;
uint32_t group;
uint32_t size;
uint32_t allocSize;
uint32_t unk[3];
uint32_t id;
uint32_t ctime;
uint32_t mtime;
uint32_t unk2[0x0D];
} FSStat;
typedef struct {
FSStat stat;
char name[0x100];
} FSDirectoryEntry;
#define DIR_ENTRY_IS_DIRECTORY 0x80000000
#define FSA_MOUNTFLAGS_BINDMOUNT (1 << 0)
#define FSA_MOUNTFLAGS_GLOBAL (1 << 1)
int FSA_Mount(int fd, const char* device_path, char* volume_path, uint32_t flags, char* arg_string, int arg_string_len);
int FSA_Unmount(int fd, const char* path, uint32_t flags);
int FSA_FlushVolume(int fd, const char* volume_path);
int FSA_GetDeviceInfo(int fd, const char* device_path, int type, uint32_t* out_data);
int FSA_MakeDir(int fd, const char* path, uint32_t flags);
int FSA_OpenDir(int fd, const char* path, int* outHandle);
int FSA_ReadDir(int fd, int handle, FSDirectoryEntry* out_data);
int FSA_RewindDir(int fd, int handle);
int FSA_CloseDir(int fd, int handle);
int FSA_ChangeDir(int fd, const char* path);
int FSA_OpenFile(int fd, const char* path, const char* mode, int* outHandle);
int FSA_ReadFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags);
int FSA_WriteFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags);
int FSA_StatFile(int fd, int handle, FSStat* out_data);
int FSA_CloseFile(int fd, int fileHandle);
int FSA_SetPosFile(int fd, int fileHandle, uint32_t position);
int FSA_GetStat(int fd, const char* path, FSStat* out_data);
int FSA_Remove(int fd, const char* path);
int FSA_ChangeMode(int fd, const char* path, int mode);
int FSA_RawOpen(int fd, const char* device_path, int* outHandle);
int FSA_RawRead(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t sector_offset, int device_handle);
int FSA_RawWrite(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t sector_offset, int device_handle);
int FSA_RawClose(int fd, int device_handle);

126
ios_mcp/source/gfx.c Normal file
View File

@ -0,0 +1,126 @@
#include "gfx.h"
#include "imports.h"
#include <stdio.h>
#include <stdarg.h>
#include "font_bin.h"
#define TV_FRAMEBUFFER ((uint32_t*) (0x14000000 + 0x3500000))
#define TV_HEIGHT 720
#define TV_STRIDE 1280
#define DRC_FRAMEBUFFER ((uint32_t*) (0x14000000 + 0x38c0000))
#define DRC_HEIGHT 480
#define DRC_STRIDE 896
#define CHAR_SIZE_X 8
#define CHAR_SIZE_Y 8
static uint32_t font_color = 0xffffffff;
void gfx_clear(uint32_t col)
{
for (uint32_t i = 0; i < TV_STRIDE * TV_HEIGHT; i++) {
TV_FRAMEBUFFER[i] = col;
}
for (uint32_t i = 0; i < DRC_STRIDE * DRC_HEIGHT; i++) {
DRC_FRAMEBUFFER[i] = col;
}
}
void gfx_draw_pixel(uint32_t x, uint32_t y, uint32_t col)
{
// put pixel in the drc buffer
uint32_t i = x + y * DRC_STRIDE;
if (i < DRC_STRIDE * DRC_HEIGHT) {
DRC_FRAMEBUFFER[i] = col;
}
// scale and put pixel in the tv buffer
for (uint32_t yy = (y * 1.5f); yy < ((y * 1.5f) + 1); yy++) {
for (uint32_t xx = (x * 1.5f); xx < ((x * 1.5f) + 1); xx++) {
uint32_t i = xx + yy * TV_STRIDE;
if (i < TV_STRIDE * TV_HEIGHT) {
TV_FRAMEBUFFER[i] = col;
}
}
}
}
void gfx_draw_rect_filled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t col)
{
for (uint32_t yy = y; yy < y + h; yy++) {
for (uint32_t xx = x; xx < x + w; xx++) {
gfx_draw_pixel(xx, yy, col);
}
}
}
void gfx_draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, uint32_t col)
{
gfx_draw_rect_filled(x, y, w, borderSize, col);
gfx_draw_rect_filled(x, y + h - borderSize, w, borderSize, col);
gfx_draw_rect_filled(x, y, borderSize, h, col);
gfx_draw_rect_filled(x + w - borderSize, y, borderSize, h, col);
}
void gfx_set_font_color(uint32_t col)
{
font_color = col;
}
uint32_t gfx_get_text_width(const char* string)
{
uint32_t i;
for (i = 0; string[i]; i++);
return i * CHAR_SIZE_X;
}
static void gfx_draw_char(uint32_t x, uint32_t y, char c)
{
if(c < 32) {
return;
}
c -= 32;
const uint8_t* charData = &font_bin[(CHAR_SIZE_X * CHAR_SIZE_Y * c) / 8];
for (uint32_t i = 0; i < CHAR_SIZE_Y; i++) {
uint8_t v = *(charData++);
for (uint32_t j = 0; j < CHAR_SIZE_X; j++) {
if(v & (1 << j)) {
gfx_draw_pixel(x + j, y + i, font_color);
}
}
}
}
void gfx_print(uint32_t x, uint32_t y, int alignRight, const char* string)
{
if (alignRight) {
x -= gfx_get_text_width(string);
}
for (uint32_t i = 0; string[i]; i++) {
if(string[i] >= 32 && string[i] < 128) {
gfx_draw_char(x + i * CHAR_SIZE_X, y, string[i]);
}
}
}
void gfx_printf(uint32_t x, uint32_t y, int alignRight, const char* format, ...)
{
va_list args;
va_start(args, format);
char buffer[0x100];
vsnprintf(buffer, sizeof(buffer), format, args);
gfx_print(x, y, alignRight, buffer);
va_end(args);
}

23
ios_mcp/source/gfx.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
// visible screen sizes
#define SCREEN_WIDTH 854
#define SCREEN_HEIGHT 480
void gfx_clear(uint32_t color);
void gfx_draw_pixel(uint32_t x, uint32_t y, uint32_t color);
void gfx_draw_rect_filled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color);
void gfx_draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, uint32_t col);
void gfx_set_font_color(uint32_t col);
uint32_t gfx_get_text_width(const char* string);
void gfx_print(uint32_t x, uint32_t y, int alignRight, const char* string);
void gfx_printf(uint32_t x, uint32_t y, int alignRight, const char* format, ...);

42
ios_mcp/source/imports.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
typedef struct {
void* ptr;
uint32_t len;
uint32_t paddr;
} IOSVec_t;
// thumb functions can't just be provided to the linker
#define setDefaultTitleId ((int (*)(uint64_t tid)) (0x0510d984 | 1))
int bspWrite(const char* entity, uint32_t instance, const char* attribute, uint32_t size, const void* buffer);
int bspRead(const char* entity, uint32_t instance, const char* attribute, uint32_t size, void* buffer);
int IOS_CreateThread(int (*fun)(void* arg), void* arg, void* stack_top, uint32_t stacksize, int priority, uint32_t flags);
int IOS_JoinThread(int threadid, int* retval);
int IOS_CancelThread(int threadid, int return_value);
int IOS_StartThread(int threadid);
int IOS_GetThreadPriority(int threadid);
int IOS_CreateMessageQueue(uint32_t* ptr, uint32_t n_msgs);
int IOS_DestroyMessageQueue(int queueid);
int IOS_ReceiveMessage(int queueid, uint32_t* message, uint32_t flags);
void* IOS_HeapAlloc(uint32_t heap, uint32_t size);
void* IOS_HeapAllocAligned(uint32_t heap, uint32_t size, uint32_t alignment);
void IOS_HeapFree(uint32_t heap, void* ptr);
int IOS_Open(const char* device, int mode);
int IOS_Close(int fd);
int IOS_Ioctl(int fd, uint32_t request, void* input_buffer, uint32_t input_buffer_len, void* output_buffer, uint32_t output_buffer_len);
int IOS_Ioctlv(int fd, uint32_t request, uint32_t vector_count_in, uint32_t vector_count_out, IOSVec_t* vector);
int IOS_IoctlvAsync(int fd, uint32_t request, uint32_t vector_count_in, uint32_t vector_count_out, IOSVec_t* vector, int callbackQueue, void* msg);
void IOS_InvalidateDCache(void* ptr, uint32_t len);
void IOS_FlushDCache(void* ptr, uint32_t len);
void IOS_Shutdown(int reset);
int IOS_Syscall0x81(int type, uint32_t address, uint32_t value);

View File

@ -0,0 +1,115 @@
#include "mcp_install.h"
#include "imports.h"
#include <string.h>
static void* allocIoBuf(uint32_t size)
{
void* ptr = IOS_HeapAlloc(0xcaff, size);
memset(ptr, 0, size);
return ptr;
}
static void freeIoBuf(void* ptr)
{
IOS_HeapFree(0xcaff, ptr);
}
int MCP_InstallGetInfo(int handle, const char* path, MCPInstallInfo* out_info)
{
uint8_t* buf = allocIoBuf(0x27f + 0x16 + (sizeof(IOSVec_t) * 2));
char* path_buf = (char*) buf + (sizeof(IOSVec_t) * 2);
strncpy(path_buf, path, 0x27f);
void* out_buf = buf + (sizeof(IOSVec_t) * 2) + 0x27f;
IOSVec_t* vecs = (IOSVec_t*) buf;
vecs[0].ptr = path_buf;
vecs[0].len = 0x27f;
vecs[1].ptr = out_buf;
vecs[1].len = 0x16;
int res = IOS_Ioctlv(handle, 0x80, 1, 1, vecs);
if (res >= 0) {
memcpy(out_info, out_buf, 0x16);
}
freeIoBuf(buf);
return res;
}
int MCP_SetTargetUsb(int handle, int target)
{
uint32_t* buf = allocIoBuf(4);
memcpy(buf, &target, 4);
int res = IOS_Ioctl(handle, 0xf1, buf, 4, NULL, 0);
freeIoBuf(buf);
return res;
}
int MCP_InstallSetTargetDevice(int handle, int device)
{
uint32_t* buf = allocIoBuf(4);
memcpy(buf, &device, 4);
int res = IOS_Ioctl(handle, 0x8d, buf, 4, NULL, 0);
freeIoBuf(buf);
return res;
}
int MCP_InstallTitle(int handle, const char* path)
{
uint8_t* buf = allocIoBuf(0x27f + sizeof(IOSVec_t));
char* path_buf = (char*) buf + sizeof(IOSVec_t);
strncpy(path_buf, path, 0x27f);
IOSVec_t* vecs = (IOSVec_t*) buf;
vecs[0].ptr = path_buf;
vecs[0].len = 0x27f;
int res = IOS_Ioctlv(handle, 0x81, 1, 0, vecs);
freeIoBuf(buf);
return res;
}
int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue, void* msg)
{
uint8_t* buf = allocIoBuf(0x27f + sizeof(IOSVec_t));
char* path_buf = (char*) buf + sizeof(IOSVec_t);
strncpy(path_buf, path, 0x27f);
IOSVec_t* vecs = (IOSVec_t*) buf;
vecs[0].ptr = path_buf;
vecs[0].len = 0x27f;
int res = IOS_IoctlvAsync(handle, 0x81, 1, 0, vecs, callbackQueue, msg);
freeIoBuf(buf);
return res;
}
int MCP_InstallGetProgress(int handle, MCPInstallProgress* progress)
{
uint32_t* buf = allocIoBuf(0x24);
int res = IOS_Ioctl(handle, 0x82, NULL, 0, buf, 0x24);
if (res >= 0) {
memcpy(progress, buf, 0x24);
}
freeIoBuf(buf);
return res;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <stdint.h>
typedef struct __attribute__((packed)) {
uint64_t titleId;
char unk[14];
} MCPInstallInfo;
typedef struct __attribute__((packed)) {
uint32_t inProgress;
uint64_t titleId;
uint64_t sizeTotal;
uint64_t sizeProgress;
uint32_t contentsTotal;
uint32_t contentsProgress;
} MCPInstallProgress;
int MCP_InstallGetInfo(int handle, const char* path, MCPInstallInfo* out_info);
int MCP_SetTargetUsb(int handle, int target);
int MCP_InstallSetTargetDevice(int handle, int device);
int MCP_InstallTitle(int handle, const char* path);
int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue, void* msg);
int MCP_InstallGetProgress(int handle, MCPInstallProgress* progress);

View File

@ -0,0 +1,22 @@
#include "imports.h"
#include "menu.h"
static int threadStarted = 0;
static uint8_t threadStack[0x1000] __attribute__((aligned(0x20)));
int MCP_ioctl100_patch(void* msg)
{
printf("MCP_ioctl100_patch %p\n", msg);
// start the menu thread
if (!threadStarted) {
int tid = IOS_CreateThread(menuThread, NULL, threadStack + sizeof(threadStack), sizeof(threadStack), IOS_GetThreadPriority(0), 1);
if (tid > 0) {
IOS_StartThread(tid);
}
threadStarted = 1;
}
return 29;
}

View File

@ -0,0 +1,10 @@
.extern MCP_ioctl100_patch
.global _MCP_ioctl100_patch
_MCP_ioctl100_patch:
.thumb
ldr r0, [r7, #0xc]
bx pc
nop
.arm
ldr r12, =MCP_ioctl100_patch
bx r12

864
ios_mcp/source/menu.c Normal file
View File

@ -0,0 +1,864 @@
/*
* Copyright (C) 2022 GaryOderNichts
*
* 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, see <https://www.gnu.org/licenses/>.
*/
#include "menu.h"
#include "imports.h"
#include "gfx.h"
#include "utils.h"
#include "fsa.h"
#include "socket.h"
#include "netconf.h"
#include "wupserver.h"
#include "mcp_install.h"
#include "ccr.h"
#include <string.h>
#include <unistd.h>
#define COLOR_BACKGROUND 0x000000ff
#define COLOR_PRIMARY 0xffffffff
#define COLOR_SECONDARY 0x3478e4ff
#define COLOR_SUCCESS 0x00ff00ff
#define COLOR_ERROR 0xff0000ff
static void option_SetColdbootTitle(void);
static void option_DumpSyslogs(void);
static void option_DumpOtpAndSeeprom(void);
static void option_StartWupserver(void);
static void option_LoadNetConf(void);
static void option_displayDRCPin(void);
static void option_InstallWUP(void);
static void option_Shutdown(void);
extern int ppcHeartBeatThreadId;
extern uint64_t currentColdbootOS;
extern uint64_t currentColdbootTitle;
static int fsaHandle = -1;
static struct {
const char* name;
void (*callback)(void);
} mainMenuOptions[] = {
{ "Set Coldboot Title", option_SetColdbootTitle },
{ "Dump Syslogs", option_DumpSyslogs },
{ "Dump OTP + SEEPROM", option_DumpOtpAndSeeprom },
{ "Start wupserver", option_StartWupserver },
{ "Load Network Configuration", option_LoadNetConf },
{ "Display DRC Pin", option_displayDRCPin },
{ "Install WUP", option_InstallWUP, },
{ "Shutdown", option_Shutdown },
};
static const int numMainMenuOptions = sizeof(mainMenuOptions) / sizeof(mainMenuOptions[0]);
static void waitButtonInput(void)
{
gfx_set_font_color(COLOR_PRIMARY);
gfx_draw_rect_filled(8, SCREEN_HEIGHT - (16 + 8 + 2), SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
gfx_printf(16, SCREEN_HEIGHT - 16, 0, "Press EJECT or POWER to proceed");
uint8_t cur_flag = 0;
uint8_t flag = 0;
while (1) {
readSystemEventFlag(&flag);
if (cur_flag != flag) {
if ((flag & SYSTEM_EVENT_FLAG_EJECT_BUTTON) || (flag & SYSTEM_EVENT_FLAG_POWER_BUTTON)) {
return;
}
cur_flag = flag;
}
}
}
static void option_SetColdbootTitle(void)
{
static const char* coldbootTitleOptions[] = {
"Back",
"Wii U Menu (JPN) - 00050010-10040000",
"Wii U Menu (USA) - 00050010-10040100",
"Wii U Menu (EUR) - 00050010-10040200",
};
int rval;
uint64_t newtid = 0;
int redraw = 1;
int selected = 0;
uint8_t cur_flag = 0;
uint8_t flag = 0;
while (1) {
readSystemEventFlag(&flag);
if (cur_flag != flag) {
if (flag & SYSTEM_EVENT_FLAG_EJECT_BUTTON) {
selected++;
if (selected == 4) {
selected = 0;
}
redraw = 1;
} else if (flag & SYSTEM_EVENT_FLAG_POWER_BUTTON) {
switch (selected) {
case 0:
return;
case 1:
newtid = 0x0005001010040000llu;
rval = setDefaultTitleId(newtid);
break;
case 2:
newtid = 0x0005001010040100llu;
rval = setDefaultTitleId(newtid);
break;
case 3:
newtid = 0x0005001010040200llu;
rval = setDefaultTitleId(newtid);
break;
}
redraw = 1;
}
cur_flag = flag;
}
if (redraw) {
gfx_clear(COLOR_BACKGROUND);
uint32_t index = 16 + 8 + 2 + 8;
// draw current titles
gfx_printf(16, index, 0, "Current coldboot title: 0x%016llx", currentColdbootTitle);
index += 8 + 4;
gfx_printf(16, index, 0, "Current coldboot os: 0x%016llx", currentColdbootOS);
index += (8 + 4) * 2;
// draw options
for (int i = 0; i < 4; i++) {
gfx_draw_rect_filled(16 - 1, index - 1,
gfx_get_text_width(coldbootTitleOptions[i]) + 2, 8 + 2,
selected == i ? COLOR_PRIMARY : COLOR_BACKGROUND);
gfx_set_font_color(selected == i ? COLOR_BACKGROUND : COLOR_PRIMARY);
gfx_printf(16, index, 0, coldbootTitleOptions[i]);
index += 8 + 4;
}
gfx_set_font_color(COLOR_PRIMARY);
if (newtid) {
index += (8 + 4) * 2;
gfx_printf(16, index, 0, "Setting coldboot title id to 0x%016llx, rval %d", newtid, rval);
index += 8 + 4;
if (rval < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Error! Make sure title is installed correctly.");
} else {
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Success!");
}
}
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Set Coldboot Title";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
// draw bottom bar
gfx_draw_rect_filled(8, SCREEN_HEIGHT - (16 + 8 + 2), SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
gfx_printf(16, SCREEN_HEIGHT - 16, 0, "EJECT: Navigate ");
gfx_printf(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 16, 1, "POWER: Choose");
redraw = 0;
}
}
}
static void option_DumpSyslogs(void)
{
gfx_clear(COLOR_BACKGROUND);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Dumping Syslogs...";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
uint32_t index = 16 + 8 + 2 + 8;
gfx_printf(16, index, 0, "Creating 'logs' directory...");
index += 8 + 4;
int res = FSA_MakeDir(fsaHandle, "/vol/storage_recovsd/logs", 0x600);
if ((res < 0) && !(res == -0x30016)) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to create directory: %x", res);
waitButtonInput();
return;
}
gfx_printf(16, index, 0, "Opening system 'logs' directory...");
index += 8 + 4;
int dir_handle;
res = FSA_OpenDir(fsaHandle, "/vol/system/logs", &dir_handle);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to open system logs: %x", res);
waitButtonInput();
return;
}
char src_path[500];
char dst_path[500];
FSDirectoryEntry dir_entry;
while (FSA_ReadDir(fsaHandle, dir_handle, &dir_entry) >= 0) {
if (dir_entry.stat.flags & DIR_ENTRY_IS_DIRECTORY) {
continue;
}
gfx_draw_rect_filled(0, index, SCREEN_WIDTH, 8, COLOR_BACKGROUND);
gfx_printf(16, index, 0, "Copying %s...", dir_entry.name);
snprintf(src_path, sizeof(src_path), "/vol/system/logs/" "%s", dir_entry.name);
snprintf(dst_path, sizeof(dst_path), "/vol/storage_recovsd/logs/" "%s", dir_entry.name);
res = copy_file(fsaHandle, src_path, dst_path);
if (res < 0) {
index += 8 + 4;
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to copy %s: %x", dir_entry.name, res);
waitButtonInput();
FSA_CloseDir(fsaHandle, dir_handle);
return;
}
}
index += 8 + 4;
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Done!");
waitButtonInput();
FSA_CloseDir(fsaHandle, dir_handle);
}
static void option_DumpOtpAndSeeprom(void)
{
gfx_clear(COLOR_BACKGROUND);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Dumping OTP + SEEPROM...";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
uint32_t index = 16 + 8 + 2 + 8;
gfx_printf(16, index, 0, "Creating otp.bin...");
index += 8 + 4;
void* dataBuffer = IOS_HeapAllocAligned(0xcaff, 0x400, 0x40);
if (!dataBuffer) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Out of memory!");
waitButtonInput();
return;
}
int otpHandle;
int res = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/otp.bin", "w", &otpHandle);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to create otp.bin: %x", res);
waitButtonInput();
IOS_HeapFree(0xcaff, dataBuffer);
return;
}
gfx_printf(16, index, 0, "Reading OTP...");
index += 8 + 4;
res = readOTP(dataBuffer, 0x400);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to read OTP: %x", res);
waitButtonInput();
FSA_CloseFile(fsaHandle, otpHandle);
IOS_HeapFree(0xcaff, dataBuffer);
return;
}
gfx_printf(16, index, 0, "Writing otp.bin...");
index += 8 + 4;
res = FSA_WriteFile(fsaHandle, dataBuffer, 1, 0x400, otpHandle, 0);
if (res != 0x400) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to write otp.bin: %x", res);
waitButtonInput();
FSA_CloseFile(fsaHandle, otpHandle);
IOS_HeapFree(0xcaff, dataBuffer);
return;
}
FSA_CloseFile(fsaHandle, otpHandle);
gfx_printf(16, index, 0, "Creating seeprom.bin...");
index += 8 + 4;
int seepromHandle;
res = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/seeprom.bin", "w", &seepromHandle);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to create seeprom.bin: %x", res);
waitButtonInput();
IOS_HeapFree(0xcaff, dataBuffer);
return;
}
gfx_printf(16, index, 0, "Reading SEEPROM...");
index += 8 + 4;
res = EEPROM_Read(0, 0x100, (uint16_t*) dataBuffer);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to read EEPROM: %x", res);
waitButtonInput();
FSA_CloseFile(fsaHandle, seepromHandle);
IOS_HeapFree(0xcaff, dataBuffer);
return;
}
gfx_printf(16, index, 0, "Writing seeprom.bin...");
index += 8 + 4;
res = FSA_WriteFile(fsaHandle, dataBuffer, 1, 0x200, seepromHandle, 0);
if (res != 0x200) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to write seeprom.bin: %x", res);
waitButtonInput();
FSA_CloseFile(fsaHandle, seepromHandle);
IOS_HeapFree(0xcaff, dataBuffer);
return;
}
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Done!");
waitButtonInput();
FSA_CloseFile(fsaHandle, seepromHandle);
IOS_HeapFree(0xcaff, dataBuffer);
}
static void option_StartWupserver(void)
{
gfx_clear(COLOR_BACKGROUND);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Running wupserver...";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
uint32_t index = 16 + 8 + 2 + 8;
gfx_printf(16, index, 0, "Initializing netconf...");
index += 8 + 4;
int res = netconf_init();
if (res <= 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to initialize netconf: %x", res);
waitButtonInput();
return;
}
gfx_printf(16, index, 0, "Waiting for network connection... 5s");
NetConfInterfaceTypeEnum interface = 0xff;
for (int i = 0; i < 5; i++) {
if (netconf_get_if_linkstate(NET_CFG_INTERFACE_TYPE_WIFI) == NET_CFG_LINK_STATE_UP) {
interface = NET_CFG_INTERFACE_TYPE_WIFI;
break;
}
if (netconf_get_if_linkstate(NET_CFG_INTERFACE_TYPE_ETHERNET) == NET_CFG_LINK_STATE_UP) {
interface = NET_CFG_INTERFACE_TYPE_ETHERNET;
break;
}
usleep(1000 * 1000);
gfx_draw_rect_filled(0, index, SCREEN_WIDTH, 8, COLOR_BACKGROUND);
gfx_printf(16, index, 0, "Waiting for network connection... %ds", 4 - i);
}
index += 8 + 4;
if (interface == 0xff) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "No network connection!");
waitButtonInput();
return;
}
gfx_printf(16, index, 0, "Connected using %s", (interface == NET_CFG_INTERFACE_TYPE_WIFI) ? "WIFI" : "ETHERNET");
index += 8 + 4;
uint8_t ip_address[4];
res = netconf_get_assigned_address(interface, (uint32_t*) ip_address);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to get IP address: %x", res);
waitButtonInput();
return;
}
gfx_printf(16, index, 0, "IP address: %d.%d.%d.%d", ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
index += 8 + 4;
res = socketInit();
if (res <= 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to initialize socketlib: %x", res);
waitButtonInput();
return;
}
wupserver_init();
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Wupserver running. Press EJECT or POWER to stop.");
index += 8 + 4;
waitButtonInput();
gfx_set_font_color(COLOR_PRIMARY);
gfx_printf(16, index, 0, "Stopping wupserver...");
index += 8 + 4;
wupserver_deinit();
}
static void network_parse_config_value(uint32_t* console_idx, NetConfCfg* cfg, const char* key, const char* value, uint32_t value_len)
{
if (strncmp(key, "type", sizeof("type")) == 0) {
gfx_printf(16, *console_idx, 0, "Type: %s", value);
(*console_idx) += 8 + 4;
if (value) {
if (strncmp(value, "wifi", sizeof("wifi")) == 0) {
cfg->wl0.if_index = NET_CFG_INTERFACE_TYPE_WIFI;
cfg->wl0.if_sate = 1;
cfg->wl0.ipv4Info.mode = NET_CONFIG_IPV4_MODE_AUTO_OBTAIN_IP;
cfg->wifi.config_method = 0;
} else if (strncmp(value, "eth", sizeof("eth")) == 0) {
cfg->eth0.if_index = NET_CFG_INTERFACE_TYPE_ETHERNET;
cfg->eth0.if_sate = 1;
cfg->eth0.ipv4Info.mode = NET_CONFIG_IPV4_MODE_AUTO_OBTAIN_IP;
cfg->ethCfg.negotiation = NET_CFG_ETH_CFG_NEGOTIATION_AUTO;
}
}
} else if (strncmp(key, "ssid", sizeof("ssid")) == 0) {
gfx_printf(16, *console_idx, 0, "SSID: %s (%d)", value, value_len);
(*console_idx) += 8 + 4;
if (value) {
memcpy(cfg->wifi.config.ssid, value, value_len);
cfg->wifi.config.ssidlength = value_len;
}
} else if (strncmp(key, "key", sizeof("key")) == 0) {
gfx_printf(16, *console_idx, 0, "Key: ******* (%d)", value_len);
(*console_idx) += 8 + 4;
if (value) {
memcpy(cfg->wifi.config.privacy.aes_key, value, value_len);
cfg->wifi.config.privacy.aes_key_len = value_len;
}
} else if (strncmp(key, "key_type", sizeof("key_type")) == 0) {
gfx_printf(16, *console_idx, 0, "Key type: %s", value);
(*console_idx) += 8 + 4;
if (value) {
if (strncmp(value, "NONE", sizeof("NONE")) == 0) {
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_NONE;
} else if (strncmp(value, "WEP", sizeof("WEP")) == 0) {
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WEP;
} else if (strncmp(value, "WPA2_PSK_TKIP", sizeof("WPA2_PSK_TKIP")) == 0) {
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_TKIP;
} else if (strncmp(value, "WPA_PSK_TKIP", sizeof("WPA_PSK_TKIP")) == 0) {
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_TKIP;
} else if (strncmp(value, "WPA2_PSK_AES", sizeof("WPA2_PSK_AES")) == 0) {
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_AES;
} else if (strncmp(value, "WPA_PSK_AES", sizeof("WPA_PSK_AES")) == 0) {
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_AES;
} else {
gfx_printf(16, *console_idx, 0, "Unknown key type!");
(*console_idx) += 8 + 4;
}
}
}
}
static void option_LoadNetConf(void)
{
gfx_clear(COLOR_BACKGROUND);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Loading network configuration...";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
uint32_t index = 16 + 8 + 2 + 8;
gfx_printf(16, index, 0, "Initializing netconf...");
index += 8 + 4;
int res = netconf_init();
if (res <= 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to initialize netconf: %x", res);
waitButtonInput();
return;
}
gfx_printf(16, index, 0, "Reading network.cfg...");
index += 8 + 4;
int cfgHandle;
res = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/network.cfg", "r", &cfgHandle);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to open network.cfg: %x", res);
waitButtonInput();
return;
}
FSStat stat;
res = FSA_StatFile(fsaHandle, cfgHandle, &stat);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to stat file: %x", res);
waitButtonInput();
FSA_CloseFile(fsaHandle, cfgHandle);
return;
}
char* cfgBuffer = (char*) IOS_HeapAllocAligned(0xcaff, stat.size + 1, 0x40);
if (!cfgBuffer) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Out of memory!");
waitButtonInput();
FSA_CloseFile(fsaHandle, cfgHandle);
return;
}
cfgBuffer[stat.size] = '\0';
res = FSA_ReadFile(fsaHandle, cfgBuffer, 1, stat.size, cfgHandle, 0);
if (res != stat.size) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to read file: %x", res);
waitButtonInput();
IOS_HeapFree(0xcaff, cfgBuffer);
FSA_CloseFile(fsaHandle, cfgHandle);
return;
}
NetConfCfg cfg;
memset(&cfg, 0, sizeof(cfg));
// parse network cfg file
const char* keyPtr = cfgBuffer;
const char* valuePtr = NULL;
for (size_t i = 0; i < stat.size; i++) {
if (cfgBuffer[i] == '=') {
cfgBuffer[i] = '\0';
valuePtr = cfgBuffer + i + 1;
} else if (cfgBuffer[i] == '\n') {
cfgBuffer[i] = '\0';
network_parse_config_value(&index, &cfg, keyPtr, valuePtr, (cfgBuffer + i) - valuePtr);
keyPtr = cfgBuffer + i + 1;
valuePtr = NULL;
}
}
// if valuePtr isn't NULL there is another option without a newline at the end
if (valuePtr) {
network_parse_config_value(&index, &cfg, keyPtr, valuePtr, (cfgBuffer + stat.size) - valuePtr);
}
gfx_printf(16, index, 0, "Applying configuration...");
index += 8 + 4;
res = netconf_set_running(&cfg);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to apply configuration: %x", res);
waitButtonInput();
IOS_HeapFree(0xcaff, cfgBuffer);
FSA_CloseFile(fsaHandle, cfgHandle);
return;
}
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Done!");
index += 8 + 4;
waitButtonInput();
IOS_HeapFree(0xcaff, cfgBuffer);
FSA_CloseFile(fsaHandle, cfgHandle);
}
static void option_displayDRCPin(void)
{
gfx_clear(COLOR_BACKGROUND);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Display DRC Pin";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
uint32_t index = 16 + 8 + 2 + 8;
gfx_printf(16, index, 0, "Reading DRH mac address...");
index += 8 + 4;
int ccrHandle = IOS_Open("/dev/ccr_cdc", 0);
if (ccrHandle < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to open /dev/ccr_cdc: %x", ccrHandle);
waitButtonInput();
return;
}
uint8_t pincode[4];
int res = CCRSysGetPincode(ccrHandle, pincode);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to get pincode: %x", res);
waitButtonInput();
IOS_Close(ccrHandle);
return;
}
static const char* symbol_names[] = {
"spade",
"heart",
"diamond",
"clubs",
};
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Pincode: %x%x%x%x (%s %s %s %s)",
pincode[0], pincode[1], pincode[2], pincode[3],
symbol_names[pincode[0]], symbol_names[pincode[1]], symbol_names[pincode[2]], symbol_names[pincode[3]]);
waitButtonInput();
IOS_Close(ccrHandle);
}
static void option_InstallWUP(void)
{
gfx_clear(COLOR_BACKGROUND);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Installing WUP";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
uint32_t index = 16 + 8 + 2 + 8;
gfx_printf(16, index, 0, "Make sure to place a valid signed WUP directly in 'sd:/install'");
index += 8 + 4;
int mcpHandle = IOS_Open("/dev/mcp", 0);
if (mcpHandle < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to open /dev/mcp: %x", mcpHandle);
waitButtonInput();
return;
}
gfx_printf(16, index, 0, "Querying install info...");
index += 8 + 4;
MCPInstallInfo info;
int res = MCP_InstallGetInfo(mcpHandle, "/vol/storage_recovsd/install", &info);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to get install info: %x", res);
waitButtonInput();
IOS_Close(mcpHandle);
return;
}
gfx_printf(16, index, 0, "Installing title: 0x%016llx...", info.titleId);
index += 8 + 4;
// only install to NAND
res = MCP_InstallSetTargetDevice(mcpHandle, 0);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "MCP_InstallSetTargetDevice: %x", res);
waitButtonInput();
IOS_Close(mcpHandle);
return;
}
res = MCP_SetTargetUsb(mcpHandle, 0);
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "MCP_InstallSetTargetDevice: %x", res);
waitButtonInput();
IOS_Close(mcpHandle);
return;
}
// TODO: async installations
res = MCP_InstallTitle(mcpHandle, "/vol/storage_recovsd/install");
if (res < 0) {
gfx_set_font_color(COLOR_ERROR);
gfx_printf(16, index, 0, "Failed to install: %x", res);
waitButtonInput();
IOS_Close(mcpHandle);
return;
}
gfx_set_font_color(COLOR_SUCCESS);
gfx_printf(16, index, 0, "Done!");
waitButtonInput();
IOS_Close(mcpHandle);
}
static void option_Shutdown(void)
{
if (fsaHandle > 0) {
// flush mlc and slc before forcing shutdown
FSA_FlushVolume(fsaHandle, "/vol/storage_mlc01");
FSA_FlushVolume(fsaHandle, "/vol/system");
// unmount sd
FSA_Unmount(fsaHandle, "/vol/storage_recovsd", 2);
IOS_Close(fsaHandle);
}
IOS_Shutdown(0);
}
int menuThread(void* arg)
{
printf("menuThread running\n");
// stop ppcHeartbeatThread and reset PPC
IOS_CancelThread(ppcHeartBeatThreadId, 0);
resetPPC();
// open fsa and mount sdcard
fsaHandle = IOS_Open("/dev/fsa", 0);
if (fsaHandle > 0) {
int res = FSA_Mount(fsaHandle, "/dev/sdcard01", "/vol/storage_recovsd", 2, NULL, 0);
if (res < 0) {
printf("Failed to mount SD: %x\n", res);
}
} else {
printf("Failed to open FSA: %x\n", fsaHandle);
}
int redraw = 1;
int selected = 0;
uint8_t cur_flag = 0;
uint8_t flag = 0;
while (1) {
readSystemEventFlag(&flag);
if (cur_flag != flag) {
if (flag & SYSTEM_EVENT_FLAG_EJECT_BUTTON) {
selected++;
if (selected == numMainMenuOptions) {
selected = 0;
}
redraw = 1;
} else if (flag & SYSTEM_EVENT_FLAG_POWER_BUTTON) {
if (mainMenuOptions[selected].callback) {
mainMenuOptions[selected].callback();
redraw = 1;
}
}
cur_flag = flag;
}
if (redraw) {
gfx_clear(COLOR_BACKGROUND);
// draw options
uint32_t index = 16 + 8 + 2 + 8;
for (int i = 0; i < numMainMenuOptions; i++) {
gfx_draw_rect_filled(16 - 1, index - 1,
gfx_get_text_width(mainMenuOptions[i].name) + 2, 8 + 2,
selected == i ? COLOR_PRIMARY : COLOR_BACKGROUND);
gfx_set_font_color(selected == i ? COLOR_BACKGROUND : COLOR_PRIMARY);
gfx_printf(16, index, 0, mainMenuOptions[i].name);
index += 8 + 4;
}
gfx_set_font_color(COLOR_PRIMARY);
// draw top bar
gfx_set_font_color(COLOR_PRIMARY);
const char* title = "Wii U Recovery Menu v0.1 by GaryOderNichts";
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
// draw bottom bar
gfx_draw_rect_filled(8, SCREEN_HEIGHT - (16 + 8 + 2), SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
gfx_printf(16, SCREEN_HEIGHT - 16, 0, "EJECT: Navigate ");
gfx_printf(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 16, 1, "POWER: Choose");
redraw = 0;
}
}
return 0;
}

20
ios_mcp/source/menu.h Normal file
View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2022 GaryOderNichts
*
* 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, see <https://www.gnu.org/licenses/>.
*/
#pragma once
int menuThread(void* arg);

163
ios_mcp/source/netconf.c Normal file
View File

@ -0,0 +1,163 @@
#include "netconf.h"
#include "imports.h"
#include <string.h>
static int ifmgr_handle = -1;
int netconf_init(void)
{
if(ifmgr_handle > 0) {
return ifmgr_handle;
}
int ret = IOS_Open("/dev/net/ifmgr/ncl", 0);
if(ret > 0) {
ifmgr_handle = ret;
}
return ret;
}
int netconf_close(void)
{
int ret = IOS_Close(ifmgr_handle);
if (ret >= 0) {
ifmgr_handle = -1;
}
return ret;
}
static void* allocIobuf(uint32_t size)
{
void* ptr = IOS_HeapAlloc(0xcaff, size);
memset(ptr, 0x00, size);
return ptr;
}
static void freeIobuf(void* ptr)
{
IOS_HeapFree(0xcaff, ptr);
}
static int netconf_get_if_data(uint16_t* if_buf, uint16_t* data)
{
if (!data) {
return -2;
}
return IOS_Ioctl(ifmgr_handle, 0x14, if_buf, 2, data, 8);
}
NetConfLinkState netconf_get_if_linkstate(NetConfInterfaceType interface)
{
uint16_t* buf = (uint16_t*) allocIobuf(0xa);
buf[0] = interface;
int res = netconf_get_if_data(buf, buf + 1);
freeIobuf(buf);
if (res >= 0) {
return buf[2];
}
return res;
}
int netconf_get_assigned_address(NetConfInterfaceType interface, uint32_t* assignedAddress)
{
uint16_t* buf = (uint16_t*) allocIobuf(8);
buf[0] = interface;
int res = IOS_Ioctl(ifmgr_handle, 0x16, buf, 2, buf + 2, 4);
if (res >= 0) {
memcpy(assignedAddress, buf + 2, 4);
}
freeIobuf(buf);
return res;
}
int netconf_set_if_admin_state(NetConfInterfaceType interface, uint16_t admin_state)
{
uint16_t* buf = (uint16_t*) allocIobuf(0x20);
buf[0] = interface;
buf[1] = admin_state;
int res = IOS_Ioctl(ifmgr_handle, 0xf, buf, 0x20, NULL, 0);
freeIobuf(buf);
return res;
}
int netconf_get_startup_profile_id(void)
{
return IOS_Ioctl(ifmgr_handle, 8, NULL, 0, NULL, 0);
}
int netconf_nv_load(uint32_t profileId)
{
uint32_t* buf = allocIobuf(4);
buf[0] = profileId;
int res = IOS_Ioctl(ifmgr_handle, 6, buf, 4, NULL, 0);
freeIobuf(buf);
return res;
}
int netconf_get_running(NetConfCfg* running)
{
uint32_t* buf = allocIobuf(0x280);
int res = IOS_Ioctl(ifmgr_handle, 4, NULL, 0, buf, 0x280);
if (res >= 0) {
memcpy(running, buf, 0x280);
}
freeIobuf(buf);
return res;
}
int netconf_set_running(NetConfCfg* config)
{
uint32_t* buf = allocIobuf(0x280);
memcpy(buf, config, 0x280);
int res = IOS_Ioctl(ifmgr_handle, 3, buf, 0x280, NULL, 0);
freeIobuf(buf);
return res;
}
int netconf_set_eth_cfg(NetConfEthCfg* config)
{
uint32_t* buf = allocIobuf(0x8);
memcpy(buf, config, 0x8);
int res = IOS_Ioctl(ifmgr_handle, 0x1a, buf, 0x8, NULL, 0);
freeIobuf(buf);
return res;
}
int netconf_set_wifi_cfg(NetConfWifiConfig* config)
{
uint32_t* buf = allocIobuf(0x70);
memcpy(buf, config, 0x70);
int res = IOS_Ioctl(ifmgr_handle, 0x11, buf, 0x70, NULL, 0);
freeIobuf(buf);
return res;
}

188
ios_mcp/source/netconf.h Normal file
View File

@ -0,0 +1,188 @@
#pragma once
#include <stdint.h>
#include <assert.h>
// Thanks @Maschell for all the types
typedef uint16_t NetConfEthCfgSpeed;
typedef uint16_t NetConfEthCfgDuplex;
typedef uint16_t NetConfEthCfgNegotiation;
typedef uint16_t NetConfWifiPrivacyMode;
typedef uint16_t NetConfProxyAuthType;
typedef uint16_t NetConfProxyStatus;
typedef uint16_t NetConfInterfaceType;
typedef struct NetConfAllProfileState NetConfAllProfileState;
typedef struct NetConfDNSInfo NetConfDNSInfo;
typedef struct NetConfEthCfg NetConfEthCfg;
typedef struct NetConfProxyConfig NetConfProxyConfig;
typedef struct NetConfValidFlags NetConfValidFlags;
typedef struct NetConfWifiConfig NetConfWifiConfig;
typedef struct NetConfOpt NetConfOpt;
typedef struct NetConfIPv4Info NetConfIPv4Info;
typedef struct NetConfMACAddr NetConfMACAddr;
typedef struct NetConfCfg NetConfCfg;
typedef struct NetConfAOSSConfig NetConfAOSSConfig;
typedef struct NetConfWifiConfigManualPrivacy NetConfWifiConfigManualPrivacy;
typedef struct NetConfWifiConfigManual NetConfWifiConfigManual;
typedef struct NetConfInterface NetConfInterface;
typedef enum NetConfInterfaceTypeEnum {
NET_CFG_INTERFACE_TYPE_WIFI = 0,
NET_CFG_INTERFACE_TYPE_ETHERNET = 1,
}NetConfInterfaceTypeEnum;
typedef enum NetConfEthCfgSpeedEnum {
NET_CFG_ETH_CFG_SPEED_10M = 10,
NET_CFG_ETH_CFG_SPEED_100M = 100,
} NetConfEthCfgSpeedEnum;
typedef enum NetConfEthCfgDuplexEnum {
NET_CFG_ETH_CFG_DUPLEX_HALF = 1,
NET_CFG_ETH_CFG_DUPLEX_FULL = 2,
}NetConfEthCfgDuplexEnum;
typedef enum NetConfEthCfgNegotiationEnum {
NET_CFG_ETH_CFG_NEGOTIATION_MANUAL = 1,
NET_CFG_ETH_CFG_NEGOTIATION_AUTO = 2,
}NetConfEthCfgNegotiationEnum;
typedef enum NetConfIPv4Mode {
NET_CONFIG_IPV4_MODE_AUTO_OBTAIN_IP = 0,
NET_CONFIG_IPV4_MODE_NO_AUTO_OBTAIN_IP = 2,
}NetConfIPv4Mode;
typedef enum NetConfWifiPrivacyModeEnum {
NET_CFG_WIFI_PRIVACY_MODE_NONE = 0,
NET_CFG_WIFI_PRIVACY_MODE_WEP = 1,
NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_TKIP = 3,
NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_TKIP = 4,
NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_AES = 5,
NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_AES = 6,
}NetConfWifiPrivacyModeEnum;
typedef enum NetConfProxyAuthTypeEnum {
NET_CFG_PROXY_AUTH_TYPE_NONE = 0,
NET_CFG_PROXY_AUTH_TYPE_BASIC_AUTHENTICATION = 1,
}NetConfProxyAuthTypeEnum;
typedef enum NetConfProxyStatusEnum {
NET_CFG_PROXY_DISABLED = 0,
NET_CFG_PROXY_ENABLED = 1,
}NetConfProxyStatusEnum;
typedef enum NetConfLinkState {
NET_CFG_LINK_STATE_UP = 1,
NET_CFG_LINK_STATE_NO_USED = 2,
NET_CFG_LINK_STATE_DOWN = 3,
}NetConfLinkState;
struct NetConfAllProfileState {
char unk[0x18];
};
struct NetConfDNSInfo {
char unk[0xE];
};
struct __attribute__((packed)) NetConfEthCfg {
NetConfEthCfgSpeed speed;
NetConfEthCfgDuplex duplex;
NetConfEthCfgNegotiation negotiation;
char padding[2];
};
struct NetConfIPv4Info {
NetConfIPv4Mode mode;
uint32_t addr;
uint32_t netmask;
uint32_t nexthop; // gateway
uint32_t ns1; // dns 1
uint32_t ns2; // dns 2
};
struct NetConfMACAddr {
uint8_t MACAddr[0x6];
};
struct NetConfProxyConfig {
NetConfProxyStatus use_proxy; // true/false
uint16_t port;
char padding[2];
NetConfProxyAuthType auth_type;
char host[0x80];
char username[0x80]; // only 0x20 bytes usable
char password[0x40]; // only 0x20 bytes usable
char noproxy_hosts[0x80]; // not used
};
struct NetConfValidFlags {
char unk[0x18];
};
struct __attribute__((packed)) NetConfWifiConfigManualPrivacy {
NetConfWifiPrivacyMode mode;
char padding[2];
uint16_t aes_key_len;
uint8_t aes_key[0x40];
char padding2[2];
};
struct __attribute__((packed)) NetConfWifiConfigManual {
uint8_t ssid[0x20];
uint16_t ssidlength;
char padding[2];
NetConfWifiConfigManualPrivacy privacy;
};
struct NetConfWifiConfig {
uint16_t config_method;
char padding[2];
NetConfWifiConfigManual config;
};
struct NetConfOpt {
char unk[0x2c1];
};
struct __attribute__((packed)) NetConfInterface {
uint16_t if_index;
uint16_t if_sate;
uint32_t if_mtu;
NetConfIPv4Info ipv4Info;
};
struct NetConfCfg {
NetConfInterface wl0;
NetConfWifiConfig wifi;
NetConfInterface eth0;
NetConfEthCfg ethCfg;
NetConfProxyConfig proxy;
};
static_assert(sizeof(NetConfCfg) == 0x280, "NetConfCfg: different size than expected");
struct NetConfAOSSConfig {
char unk[0x1b0];
};
int netconf_init(void);
int netconf_close(void);
NetConfLinkState netconf_get_if_linkstate(NetConfInterfaceType interface);
int netconf_get_assigned_address(NetConfInterfaceType interface, uint32_t* assignedAddress);
int netconf_set_if_admin_state(NetConfInterfaceType interface, uint16_t admin_state);
int netconf_get_startup_profile_id(void);
int netconf_nv_load(uint32_t profileId);
int netconf_get_running(NetConfCfg* running);
int netconf_set_running(NetConfCfg* config);
int netconf_set_eth_cfg(NetConfEthCfg* config);
int netconf_set_wifi_cfg(NetConfWifiConfig* config);

222
ios_mcp/source/socket.c Normal file
View File

@ -0,0 +1,222 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "socket.h"
#include "imports.h"
static int socket_handle = -1;
int socketInit()
{
if(socket_handle > 0) {
return socket_handle;
}
int ret = IOS_Open("/dev/socket", 0);
if(ret > 0) {
socket_handle = ret;
}
return ret;
}
int socketExit()
{
int ret = IOS_Close(socket_handle);
if (ret >= 0) {
socket_handle = -1;
}
return ret;
}
static void* allocIobuf(uint32_t size)
{
void* ptr = IOS_HeapAlloc(0xcaff, size);
memset(ptr, 0x00, size);
return ptr;
}
static void freeIobuf(void* ptr)
{
IOS_HeapFree(0xcaff, ptr);
}
int socket(int domain, int type, int protocol)
{
uint8_t* iobuf = allocIobuf(0xC);
uint32_t* inbuf = (uint32_t*)iobuf;
inbuf[0] = domain;
inbuf[1] = type;
inbuf[2] = protocol;
int ret = IOS_Ioctl(socket_handle, 0x11, inbuf, 0xC, NULL, 0);
freeIobuf(iobuf);
return ret;
}
int closesocket(int sockfd)
{
uint8_t* iobuf = allocIobuf(0x4);
uint32_t* inbuf = (uint32_t*)iobuf;
inbuf[0] = sockfd;
int ret = IOS_Ioctl(socket_handle, 0x3, inbuf, 0x4, NULL, 0);
freeIobuf(iobuf);
return ret;
}
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
uint8_t* iobuf = allocIobuf(0x18);
uint32_t* inbuf = (uint32_t*)iobuf;
uint32_t* outbuf = (uint32_t*)inbuf;
inbuf[0] = sockfd;
int ret = -1;
if(addr && addrlen && *addrlen == 0x10) {
inbuf[5] = *addrlen;
ret = IOS_Ioctl(socket_handle, 0x1, inbuf, 0x18, outbuf, 0x18);
if(ret >= 0) {
memcpy(addr, &outbuf[1], outbuf[5]);
*addrlen = outbuf[5];
}
} else {
inbuf[5] = 0x10;
ret = IOS_Ioctl(socket_handle, 0x1, inbuf, 0x18, outbuf, 0x18);
}
freeIobuf(iobuf);
return ret;
}
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
if(addrlen != 0x10) return -1;
uint8_t* iobuf = allocIobuf(0x18);
uint32_t* inbuf = (uint32_t*)iobuf;
inbuf[0] = sockfd;
memcpy(&inbuf[1], addr, addrlen);
inbuf[5] = addrlen;
int ret = IOS_Ioctl(socket_handle, 0x2, inbuf, 0x18, NULL, 0);
freeIobuf(iobuf);
return ret;
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
if(addrlen != 0x10) return -1;
uint8_t* iobuf = allocIobuf(0x18);
uint32_t* inbuf = (uint32_t*)iobuf;
inbuf[0] = sockfd;
memcpy(&inbuf[1], addr, addrlen);
inbuf[5] = addrlen;
int ret = IOS_Ioctl(socket_handle, 0x4, inbuf, 0x18, NULL, 0);
freeIobuf(iobuf);
return ret;
}
int listen(int sockfd, int backlog)
{
uint8_t* iobuf = allocIobuf(0x8);
uint32_t* inbuf = (uint32_t*)iobuf;
inbuf[0] = sockfd;
inbuf[1] = backlog;
int ret = IOS_Ioctl(socket_handle, 0xA, inbuf, 0x8, NULL, 0);
freeIobuf(iobuf);
return ret;
}
int shutdown(int sockfd, int how)
{
uint8_t* iobuf = allocIobuf(0x8);
uint32_t* inbuf = (uint32_t*)iobuf;
inbuf[0] = sockfd;
inbuf[1] = how;
int ret = IOS_Ioctl(socket_handle, 0x10, inbuf, 0x8, NULL, 0);
freeIobuf(iobuf);
return ret;
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
if(!len) return -101;
// TODO : size checks, split up data into multiple vectors if necessary
void* data_buf = IOS_HeapAllocAligned(0xcaff, len, 0x40);
if(!data_buf) return -100;
uint8_t* iobuf = allocIobuf(0x38);
IOSVec_t* iovec = (IOSVec_t*)iobuf;
uint32_t* inbuf = (uint32_t*)&iobuf[0x30];
inbuf[0] = sockfd;
inbuf[1] = flags;
iovec[0].ptr = inbuf;
iovec[0].len = 0x8;
iovec[1].ptr = (void*)data_buf;
iovec[1].len = len;
int ret = IOS_Ioctlv(socket_handle, 0xC, 1, 3, iovec);
if(ret > 0 && buf) {
memcpy(buf, data_buf, ret);
}
freeIobuf(data_buf);
freeIobuf(iobuf);
return ret;
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
if(!buf || !len) return -101;
// TODO : size checks, split up data into multiple vectors if necessary
void* data_buf = IOS_HeapAllocAligned(0xcaff, len, 0x40);
if(!data_buf) return -100;
uint8_t* iobuf = allocIobuf(0x38);
IOSVec_t* iovec = (IOSVec_t*)iobuf;
uint32_t* inbuf = (uint32_t*)&iobuf[0x30];
memcpy(data_buf, buf, len);
inbuf[0] = sockfd;
inbuf[1] = flags;
iovec[0].ptr = inbuf;
iovec[0].len = 0x8;
iovec[1].ptr = (void*)data_buf;
iovec[1].len = len;
int ret = IOS_Ioctlv(socket_handle, 0xE, 4, 0, iovec);
freeIobuf(data_buf);
freeIobuf(iobuf);
return ret;
}

123
ios_mcp/source/socket.h Normal file
View File

@ -0,0 +1,123 @@
#pragma once
// slightly stolen from ctrulib
#include <stdint.h>
#include <stdio.h>
#define SOL_SOCKET 0xFFFF
#define PF_UNSPEC 0
#define PF_INET 2
#define PF_INET6 10
#define AF_UNSPEC PF_UNSPEC
#define AF_INET PF_INET
#define AF_INET6 PF_INET6
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define MSG_CTRUNC 0x01000000
#define MSG_DONTROUTE 0x02000000
#define MSG_EOR 0x04000000
#define MSG_OOB 0x08000000
#define MSG_PEEK 0x10000000
#define MSG_TRUNC 0x20000000
#define MSG_WAITALL 0x40000000
#define SHUT_RD 0
#define SHUT_WR 1
#define SHUT_RDWR 2
#define SO_DEBUG 0x0001
#define SO_ACCEPTCONN 0x0002
#define SO_REUSEADDR 0x0004
#define SO_KEEPALIVE 0x0008
#define SO_DONTROUTE 0x0010
#define SO_BROADCAST 0x0020
#define SO_USELOOPBACK 0x0040
#define SO_LINGER 0x0080
#define SO_OOBINLINE 0x0100
#define SO_REUSEPORT 0x0200
#define SO_SNDBUF 0x1001
#define SO_RCVBUF 0x1002
#define SO_SNDLOWAT 0x1003
#define SO_RCVLOWAT 0x1004
#define SO_SNDTIMEO 0x1005
#define SO_RCVTIMEO 0x1006
#define SO_ERROR 0x1007
#define SO_TYPE 0x1008
#define INADDR_ANY 0x00000000
#define INADDR_BROADCAST 0xFFFFFFFF
#define INADDR_NONE 0xFFFFFFFF
#define INET_ADDRSTRLEN 16
#define INADDR_LOOPBACK 0x7f000001
#define INADDR_ANY 0x00000000
#define INADDR_BROADCAST 0xFFFFFFFF
#define INADDR_NONE 0xFFFFFFFF
#define INET_ADDRSTRLEN 16
#define IPPROTO_IP 0 /* dummy for IP */
#define IPPROTO_UDP 17 /* user datagram protocol */
#define IPPROTO_TCP 6 /* tcp */
#define IP_TOS 7
#define IP_TTL 8
#define IP_MULTICAST_LOOP 9
#define IP_MULTICAST_TTL 10
#define IP_ADD_MEMBERSHIP 11
#define IP_DROP_MEMBERSHIP 12
typedef uint32_t socklen_t;
typedef uint16_t sa_family_t;
struct sockaddr {
sa_family_t sa_family;
char sa_data[];
};
struct sockaddr_storage {
sa_family_t ss_family;
char __ss_padding[14];
};
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
struct sockaddr_in {
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct linger {
int l_onoff;
int l_linger;
};
int socketInit();
int socketExit();
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int closesocket(int sockfd);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int listen(int sockfd, int backlog);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int shutdown(int sockfd, int how);
int socket(int domain, int type, int protocol);
int sockatmark(int sockfd);

92
ios_mcp/source/utils.c Normal file
View File

@ -0,0 +1,92 @@
#include "utils.h"
#include "imports.h"
#include "fsa.h"
#define COPY_BUFFER_SIZE 1024
#define HW_RSTB 0x0d800194
uint32_t kernRead32(uint32_t address)
{
return IOS_Syscall0x81(0, address, 0);
}
void kernWrite32(uint32_t address, uint32_t value)
{
IOS_Syscall0x81(1, address, value);
}
int readOTP(void* buf, uint32_t size)
{
return IOS_Syscall0x81(2, (uint32_t) buf, size);
}
int EEPROM_Read(uint16_t offset, uint16_t num, uint16_t* buf)
{
if (offset + num > 0x100) {
return -0x1d;
}
for (uint16_t i = offset; i < offset + num; i++) {
uint16_t tmp;
int res = bspRead("EE", i, "access", 2, &tmp);
if (res < 0) {
return res;
}
*buf = tmp;
buf++;
}
return 0;
}
int resetPPC(void)
{
// cannot reset ppc through bsp?
// uint8_t val = 1;
// return bspWrite("PPC", 0, "Exe", 1, &val);
kernWrite32(HW_RSTB, kernRead32(HW_RSTB) & ~0x210);
return 0;
}
int readSystemEventFlag(uint8_t* flag)
{
return bspRead("SMC", 0, "SystemEventFlag", 1, flag);
}
int copy_file(int fsaFd, const char* src, const char* dst)
{
int readHandle;
int res = FSA_OpenFile(fsaFd, src, "r", &readHandle);
if (res < 0) {
return res;
}
int writeHandle;
res = FSA_OpenFile(fsaFd, dst, "w", &writeHandle);
if (res < 0) {
FSA_CloseFile(fsaFd, readHandle);
return res;
}
void* dataBuffer = IOS_HeapAllocAligned(0xcaff, COPY_BUFFER_SIZE, 0x40);
if (!dataBuffer) {
FSA_CloseFile(fsaFd, readHandle);
FSA_CloseFile(fsaFd, writeHandle);
return -1;
}
while ((res = FSA_ReadFile(fsaFd, dataBuffer, 1, COPY_BUFFER_SIZE, readHandle, 0)) > 0) {
if ((res = FSA_WriteFile(fsaFd, dataBuffer, 1, res, writeHandle, 0)) < 0) {
break;
}
}
IOS_HeapFree(0xcaff, dataBuffer);
FSA_CloseFile(fsaFd, writeHandle);
FSA_CloseFile(fsaFd, readHandle);
return (res > 0) ? 0 : res;
}

24
ios_mcp/source/utils.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#define SYSTEM_EVENT_FLAG_WAKE1 0x01
#define SYSTEM_EVENT_FLAG_WAKE0 0x02
#define SYSTEM_EVENT_FLAG_BT_INTERRUPT 0x04
#define SYSTEM_EVENT_FLAG_TIMER_SIGNAL 0x08
#define SYSTEM_EVENT_FLAG_DISC_INSERT 0x10
#define SYSTEM_EVENT_FLAG_EJECT_BUTTON 0x20
#define SYSTEM_EVENT_FLAG_POWER_BUTTON 0x40
uint32_t kernRead32(uint32_t address);
void kernWrite32(uint32_t address, uint32_t value);
int readOTP(void* buf, uint32_t size);
int EEPROM_Read(uint16_t offset, uint16_t num, uint16_t* buf);
int resetPPC(void);
int readSystemEventFlag(uint8_t* flag);
int copy_file(int fsaFd, const char* src, const char* dst);

206
ios_mcp/source/wupserver.c Normal file
View File

@ -0,0 +1,206 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "imports.h"
#include "socket.h"
#include "wupserver.h"
#define MCP_SVC_BASE ((void*) 0x050567ec)
static int serverRunning = 0;
static int serverSocket = -1;
static int threadId = -1;
static uint8_t threadStack[0x1000] __attribute__((aligned(0x20)));
// overwrites command_buffer with response
// returns length of response (or 0 for no response, negative for error)
static int serverCommandHandler(uint32_t* command_buffer, uint32_t length)
{
if(!command_buffer || !length) return -1;
int out_length = 4;
switch (command_buffer[0]) {
case 0: {
// write
// [cmd_id][addr]
void* dst = (void*)command_buffer[1];
memcpy(dst, &command_buffer[2], length - 8);
}
break;
case 1: {
// read
// [cmd_id][addr][length]
void* src = (void*)command_buffer[1];
length = command_buffer[2];
memcpy(&command_buffer[1], src, length);
out_length = length + 4;
}
break;
case 2: {
// svc
// [cmd_id][svc_id]
int svc_id = command_buffer[1];
int size_arguments = length - 8;
uint32_t arguments[8];
memset(arguments, 0x00, sizeof(arguments));
memcpy(arguments, &command_buffer[2], (size_arguments < 8 * 4) ? size_arguments : (8 * 4));
// return error code as data
out_length = 8;
command_buffer[1] = ((int (*const)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))(MCP_SVC_BASE + svc_id * 8))
(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]);
}
break;
case 3: {
// kill
// [cmd_id]
wupserver_deinit();
}
break;
case 4: {
// memcpy
// [dst][src][size]
void* dst = (void*)command_buffer[1];
void* src = (void*)command_buffer[2];
int size = command_buffer[3];
memcpy(dst, src, size);
}
break;
case 5: {
// repeated-write
// [address][value][n]
uint32_t* dst = (uint32_t*)command_buffer[1];
uint32_t* cache_range = (uint32_t*)(command_buffer[1] & ~0xFF);
uint32_t value = command_buffer[2];
uint32_t n = command_buffer[3];
uint32_t old = *dst;
for (int i = 0; i < n; i++) {
if (*dst != old) {
if (*dst == 0x0) {
old = *dst;
} else {
*dst = value;
IOS_FlushDCache(cache_range, 0x100);
break;
}
} else {
IOS_InvalidateDCache(cache_range, 0x100);
usleep(50);
}
}
}
break;
default:
// unknown command
return -2;
break;
}
// no error !
command_buffer[0] = 0;
return out_length;
}
static void serverClientHandler(int sock)
{
uint32_t command_buffer[0x180];
while (serverRunning) {
int ret = recv(sock, command_buffer, sizeof(command_buffer), 0);
if (ret <= 0) {
break;
}
ret = serverCommandHandler(command_buffer, ret);
if (ret > 0) {
send(sock, command_buffer, ret, 0);
} else if (ret < 0) {
send(sock, &ret, sizeof(int), 0);
}
}
closesocket(sock);
}
static void serverListenClients()
{
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = 1337;
server.sin_addr.s_addr = 0;
if (bind(serverSocket, (struct sockaddr*) &server, sizeof(server)) < 0) {
closesocket(serverSocket);
serverSocket = -1;
return;
}
if (listen(serverSocket, 1) < 0) {
closesocket(serverSocket);
serverSocket = -1;
return;
}
while (serverRunning) {
int csock = accept(serverSocket, NULL, NULL);
if (csock < 0) {
break;
}
serverClientHandler(csock);
}
closesocket(serverSocket);
serverSocket = -1;
}
static int wupserver_thread(void *arg)
{
while (1) {
if (!serverRunning) {
break;
}
serverListenClients();
usleep(1000 * 1000);
}
return 0;
}
void wupserver_init(void)
{
if (!serverRunning) {
serverSocket = -1;
threadId = IOS_CreateThread(wupserver_thread, NULL, threadStack + sizeof(threadStack), sizeof(threadStack), IOS_GetThreadPriority(0), 0);
if(threadId >= 0) {
IOS_StartThread(threadId);
serverRunning = 1;
}
}
}
void wupserver_deinit(void)
{
if (serverRunning) {
serverRunning = 0;
if (serverSocket >= 0) {
shutdown(serverSocket, SHUT_RDWR);
}
IOS_JoinThread(threadId, NULL);
}
}

View File

@ -0,0 +1,5 @@
#pragma once
void wupserver_init(void);
void wupserver_deinit(void);

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB