Universal IOS [Vega]
#1
Universal IOS [Vega]

Licensed under GPLv2 (notice of license in source)

Code creator: Vega
Code credits: Segher, Tmbinc, & Bushing (authors of the original ipc.c on WiiBrew)

Region-Free; Works for all Wii games. If using this source for a code on Dolphin, this may not work with C0 codetype (this is a Dolphin bug, nothing I can do to fix it...). Use C2 codetype instead.

Need a file editing code to be region free?
Tired of having to find the IOS symbols for your Wii game?
Tired of Wiimmfi blocking IOS_open? (though this can easily be patched by a security update)

Well the snippet of source below is a universal IOS function. It does not depend on any functions that already exist in your Wii game. It is completely self contained and relies on nothing. To achieve this, the IPC registers and IRQ handler are utilized. Just add this snippet of source (as a function call; function label name is universal_ios_function) to your current source. Call the function with the proper args and check return values according to your needs.

The way this source utilizes IPC+IRQ is 'hacky'. It's not 100% correct to how Wii games or HBC apps exactly do it. But it's 'good enough' to get the job done and keep this source as short as possible. Therefore, this source should NOT be assumed to be as dependable as plain jane in-game IOS function calls.

Args for Universal IOS function:
r3 = IOS command (see values below)
r4 = Pointer to IOS structure (this is a 0x40-byte allocated space in memory of your liking, it MUST be 32 byte aligned)
r5 = IOS's arg 1
r6 = IOS's arg 2 (if applicable)
r7 = arg 3 (if applicable)
... ...
r10 = arg 6 (if applicable)

Return values for Universal IOS
All standard IOS return values apply, however I did add one extra custom return value just in case...
-5 = Starlet hasn't received the IOS structure pointer (reply pointer was null; there was a previous IPC call already in the making???)

I could have added like 20 more custom return values (due to basic checks for IOS command, basic check for IOS structure alignment, and various checks for specific IOS stuff before calling IPC), but I figured whoever uses this source should be well rehearsed in ASM to know what they are doing.

Btw, you can use this for ISFS work (such as Create File, Delete File, etc) as long as you know all the Ioctl args and buffer stuctures.

Another fyi: If you are using this for opening NAND files (like ISFS does), make sure /dev/fs (permissions arg of 0) is open beforehand. Most Wii games do this on the very first few frames after boot. If you are using this in a C0 code, the C0 code executes before /dev/fs is opened by the game, so you will need to open /dev/fs beforehand unless you do some delayed execution like with a button activator.

r3 IOS command values:
1 = Open
2 = Close
3 = Read
4 = Write
5 = Seek
6 = Ioctl
7 = Ioctlv

~Source~

Code:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// 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 2.0 for more details.

#Start Assembly

#Prologue
universal_ios_function:
stwu sp, -0x0010 (sp)
mflr r0
stw r0, 0x0014 (sp)
stmw r30, 0x8 (sp)

#Disable External Interrupts
mfmsr r0
rlwinm r11, r0, 0, 17, 15 #Flip off 'Allow interrupts' bit
mtmsr r11 #Update MSR
rlwinm r12, r0, 17, 31, 31 #Backup modified MSR state

#Backup 1st two Args
mr r31, r3
mr r30, r4

#r5 thru r8 remain untouched before final subroutine involving IPC, no need to back up them
#r9 and r10 remain untouched the entire time, no need to back them up

#Determine what IOS command is being utilized
cmpwi r31, 1
beq- universal_open
cmpwi r31, 2
beq- universal_close
cmpwi r31, 5
beq- universal_seek
cmpwi r31, 6
beq- universal_ioctl
cmpwi r31, 7
beq- universal_ioctlv

#Read/Write is being used
universal_readwrite:
#r5 = fd
#r6 = Input/Output Buffer
#r7 = Size

#Check if read (invalidate) or write (flush), adjust cache accordingly for IOS's 2nd arg
mr r3, r6
mr r4, r7
cmpwi r31, 3
beq- its_read

#IOS_Write is being used, flush the input buffer
bl data_cache_flush
b done_with_cache

#IOS_Read is being used, invalidate the output buffer
its_read:
bl data_cache_invalidate

#Store fd, and other args to IOS structure; any mem pointer that is sent will be sent physically
done_with_cache:
stw r5, 0x8 (r30)
clrlwi r0, r6, 1
stw r0, 0xC (r30)
stw r7, 0x10 (r30)

#Prep IOS structure, run IPC
b flush_ios_structure_then_run_ipc

#IOS_Open
universal_open:
#r5 = Pointer
#r6 = Permissions

#Figure out file path byte length
addi r3, r5, -1
li r4, 0 #Use r3 for counter for upcoming subroutine call (has 1 arg)
file_path_counter:
addi r4, r4, 1
lbzu r0, 0x1 (r3)
cmpwi r0, 0
bne+ file_path_counter

#Flush file path; r4 already set
mr r3, r5
bl data_cache_flush

#Store args to IOS structure; any mem pointer that is sent will be sent physically
clrlwi r0, r5, 1
stw r0, 0xC (r30) #No fd to store at 0x8
stw r6, 0x10 (r30)

#Prep IOS structure, run IPC
b flush_ios_structure_then_run_ipc

#IOS_Close
universal_close:
#r5 = fd
#Store fd to IOS structure
stw r5, 0x8 (r30)

#Prep IOS structure, run IPC
b flush_ios_structure_then_run_ipc

#IOS_Seek
universal_seek:
#r5 = fd
#r6 = Offset from Location Mode
#r7 = Location Mode

#Store fd and other args to IOS structure
stw r5, 0x8 (r30)
stw r6, 0xC (r30)
stw r7, 0x10 (r30)
b flush_ios_structure_then_run_ipc

#IOS_Ioctl
universal_ioctl:
#r5 = fd
#r6 = ioctl_no
#r7 = inbuf
#r8 = size of inbuf
#r9 = outbuf
#r10 = size of outbuf

#Flush Input Buffer
mr r3, r7
mr r4, r8
bl data_cache_flush

#Invalidate Output Buffer
mr r3, r9
mr r4, r10
bl data_cache_invalidate

#Store fd and other args to IOS structure; any mem pointer that is sent will be sent physically
stw r5, 0x8 (r30)
stw r6, 0xC (r30)
clrlwi r0, r7, 1
stw r0, 0x10 (r30)
stw r8, 0x14 (r30)
clrlwi r0, r9, 1
stw r0, 0x18 (r30)
stw r10, 0x1C (r30)
b flush_ios_structure_then_run_ipc

#IOS_Ioctlv
universal_ioctlv:
#r5 = fd
#r6 = ioctl_no
#r7 = number of input buffers
#r8 = number of output buffers
#r9 = pointer to Vector table

#Flush entire Vector Table
mr r3, r9
add r4, r7, r8 #Input + Output Buffers
bl data_cache_flush

#Store fd and other args to IOS structure; any mem pointer that is sent will be sent physically
stw r5, 0x8 (r30)
stw r6, 0xC (r30)
stw r7, 0x10 (r30)
stw r8, 0x14 (r30)
clrlwi r0, r9, 1
stw r0, 0x18 (r30)
b flush_ios_structure_then_run_ipc

#Subroutine for data cache flush
data_cache_flush:
rlwinm r11, r3, 0, 27, 31
add r4, r4, r11
addi r4, r4, 31
rlwinm r0, r4, 27, 5, 31
mtctr r0
flush_loop:
dcbf 0, r3
addi r3, r3, 32 #Move on to next cache block (each block is 0x20 in size)
bdnz+ flush_loop
sc #Update HID0
blr

#Subroutine for data cache invalidate
data_cache_invalidate:
rlwinm r11, r3, 0, 27, 31
add r4, r4, r11
addi r4, r4, 31
rlwinm r0, r4, 27, 5, 31
mtctr r0
inval_loop:
dcbi 0, r3
addi r3, r3, 32 #Move on to next cache block (each block is 0x20 in size)
bdnz+ inval_loop
blr

#Flush first 0x20 bytes of IOS structure
flush_ios_structure_then_run_ipc:
stw r31, 0 (r30) #Store IOS command
mr r3, r30
li r4, 0x20
bl data_cache_flush

#Run the IPC Engine

#Set Hollywood IPC/IRQ upper 16 bits
lis r6, 0xCD00 #You think this would be 0xCD80, but this is how Wii games do it.

#Send IOS structure pointer (physical) to IPC_PPCMSG
clrlwi r0, r30, 1
stw r0, 0 (r6)

#Do NOT modify the IY1 and/or IY2 bits at any time.

#Tell IPC_PPCCTRL to execute command
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0001
stw r0, 0x4 (r6)

#Wait for Starlet to acknowledge our command
check_for_acknowledge:
lwz r0, 0x4 (r6)
andi. r0, r0, 0x22 #Check if IY2 and Y2 bits were flipped high by Starlet
cmpwi r0, 0x22
bne- check_for_acknowledge

#Starlet has acknowledged, rewrite Acknowledge bit flipped high to clear it
#Then disable IRQ-30, and now wait for Starlet to execute the command & have pointer (physical IOS structure) avialable in IPC_ARMMSG
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0002
stw r0, 0x4 (r6)

lis r5, 0x4000 #Keep value in r5 for later secondary use
stw r5, 0x30 (r6)

check_if_executed:
lwz r0, 0x4 (r6)
andi. r0, r0, 0x14
cmpwi r0, 0x14
bne- check_if_executed

#Check if pointer is available in IPC_ARMMSG
lwz r4, 0x8 (r6) #Using r4 due to upcoming addis instruction
cmpwi r4, 0
li r3, -5
beq- restore_interrupts #No pointer, somehow a different IPC call was being done during our work?

#Turn IOS structure pointer back to virtual, then invalidate the first 0x20 bytes
addis r3, r4, 0x8000 #Could also just use mr r4, r30 here, w/e works
li r4, 0x20
bl data_cache_invalidate

#Starlet has executed our command, rewrite Execute bit flipped high to clear it
#Then disable IRQ-30 one more time, is is needed or else next IOS call keep IPC_PPCCTRL at 0x31 forever...
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0004
stw r0, 0x4 (r6)
stw r5, 0x30 (r6)

#Set IPC_PPCMSG X bits to relaunch
lwz r0, 0x4 (r6)
clrrwi r0, r0, 4
ori r0, r0, 0x0008
stw r0, 0x4 (r6)

#Put return value into r3
lwz r3, 0x4 (r30)

#Restore External Interrupts
restore_interrupts:
cmpwi r12, 0
mfmsr r4
beq- clear_bit
ori r0, r4, 0x8000 #Flip on 'Allow Interrupts' bit
b skip_clear_bit
clear_bit:
rlwinm r0, r4, 0, 17, 15
skip_clear_bit:
mtmsr r0

#Epilogue; r3 contains return value
epilogue:
lmw r30, 0x8 (sp)
lwz r0, 0x0014 (sp)
mtlr r0
addi sp, sp, 0x0010
blr
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)