├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── CMakeLists.txt
├── README.md
├── include
└── kuio.h
├── kuio.yml
└── main.c
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: Rinnegatamante
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
3 | # Windows image file caches
4 | Thumbs.db
5 | ehthumbs.db
6 |
7 | # Folder config file
8 | Desktop.ini
9 |
10 | # Recycle Bin used on file shares
11 | $RECYCLE.BIN/
12 |
13 | # Windows Installer files
14 | *.cab
15 | *.msi
16 | *.msm
17 | *.msp
18 |
19 | # Windows shortcuts
20 | *.lnk
21 |
22 | # =========================
23 | # Operating System Files
24 | # =========================
25 |
26 | # OSX
27 | # =========================
28 |
29 | .DS_Store
30 | .AppleDouble
31 | .LSOverride
32 |
33 | # Thumbnails
34 | ._*
35 |
36 | # Files that might appear in the root of a volume
37 | .DocumentRevisions-V100
38 | .fseventsd
39 | .Spotlight-V100
40 | .TemporaryItems
41 | .Trashes
42 | .VolumeIcon.icns
43 |
44 | # Directories potentially created on remote AFP share
45 | .AppleDB
46 | .AppleDesktop
47 | Network Trash Folder
48 | Temporary Items
49 | .apdisk
50 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8)
2 |
3 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
4 | if(DEFINED ENV{VITASDK})
5 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file")
6 | else()
7 | message(FATAL_ERROR "Please define VITASDK to point to your SDK path!")
8 | endif()
9 | endif()
10 |
11 | project(kuio)
12 | include("${VITASDK}/share/vita.cmake" REQUIRED)
13 |
14 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q -Wall -O3 -std=gnu99")
15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions")
16 |
17 | include_directories(
18 | )
19 |
20 | link_directories(
21 | ${CMAKE_CURRENT_BINARY_DIR}
22 | )
23 |
24 | add_definitions(-DRELEASE)
25 |
26 | add_executable(kuio main.c)
27 |
28 | target_link_libraries(kuio
29 | SceFios2KernelForDriver_stub
30 | SceIofilemgrForDriver_stub
31 | SceThreadmgrForDriver_stub
32 | SceSblACMgrForDriver_stub
33 | SceSysmemForDriver_stub
34 | SceSysclibForDriver_stub
35 | )
36 |
37 | set_target_properties(kuio
38 | PROPERTIES LINK_FLAGS "-nostdlib"
39 | COMPILE_FLAGS "-D__VITA_KERNEL__"
40 | )
41 |
42 | add_custom_target(kuio.skprx ALL
43 | COMMAND vita-elf-create -s -e ${CMAKE_SOURCE_DIR}/kuio.yml kuio kuio.velf ${CMAKE_SOURCE_DIR}/taihen.json
44 | COMMAND vita-make-fself -c kuio.velf kuio.skprx
45 | )
46 |
47 | add_dependencies(kuio.skprx kuio)
48 |
49 | vita_create_stubs(stubs kuio ${CMAKE_SOURCE_DIR}/kuio.yml KERNEL)
50 |
51 | install(FILES ${CMAKE_BINARY_DIR}/stubs/libkuio_stub.a DESTINATION lib)
52 | install(FILES ${CMAKE_BINARY_DIR}/stubs/libkuio_stub_weak.a DESTINATION lib)
53 | install(FILES ${CMAKE_SOURCE_DIR}/include/kuio.h DESTINATION include)
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | kuio is a lightweight kernel module for taiHen that allows user modules to access ux0:/data/ for basic I/O operations.
4 | It currently gives abstractions for these functions:
5 |
6 | *sceIoOpen* -> *kuIoOpen*
7 | *sceIoWrite* -> *kuIoWrite*
8 | *sceIoRead* -> *kuIoRead*
9 | *sceIoClose* -> *kuIoClose*
10 | *sceIoLseek* -> *kuIoLseek*
11 | *sceIoRemove* -> *kuIoRemove*
12 | *sceIoMkdir* -> *kuIoMkdir*
13 | *sceIoRmdir* -> *kuIoRmdir*
14 | *ftell* -> *kuIoTell* (kuIoLseek doesn't return position)
15 |
16 | # Credits
17 |
18 | Thanks to everyone who helped me during this journey trying to get SD access on user modules on #vitasdk and #henkaku. (noname120, xerpi, yifanlu, davee, xyz, frangarcj)
19 |
--------------------------------------------------------------------------------
/include/kuio.h:
--------------------------------------------------------------------------------
1 | #ifndef _KUIO_H_
2 | #define _KUIO_H_
3 |
4 | /**
5 | * Open or create a file for reading or writing
6 | *
7 | * @par Example1: Open a file for reading
8 | * @code
9 | * SceUID fd;
10 | * kuIoOpen("device:/path/to/file", SCE_O_RDONLY, &fd)
11 | * if(!(fd) {
12 | * // error
13 | * }
14 | * @endcode
15 | *
16 | * @param file - Pointer to a string holding the name of the file to open
17 | * @param flags - Libc styled flags that are or'ed together
18 | * @param res - Pointer to where to save the returned file descriptor.
19 | *
20 | */
21 | int kuIoOpen(const char *file, int flags, SceUID* res);
22 |
23 | /**
24 | * Delete a descriptor
25 | *
26 | * @code
27 | * kuIoClose(fd);
28 | * @endcode
29 | *
30 | * @param fd - File descriptor to close
31 | *
32 | */
33 | int kuIoClose(SceUID fd);
34 |
35 | /**
36 | * Read input
37 | *
38 | * @par Example:
39 | * @code
40 | * kuIoRead(fd, data, 100);
41 | * @endcode
42 | *
43 | * @param fd - Opened file descriptor to read from
44 | * @param data - Pointer to the buffer where the read data will be placed
45 | * @param size - Size of the read in bytes
46 | *
47 | */
48 | int kuIoRead(SceUID fd, void *data, SceSize size);
49 |
50 | /**
51 | * Write output
52 | *
53 | * @par Example:
54 | * @code
55 | * kuIoWrite(fd, data, 100);
56 | * @endcode
57 | *
58 | * @param fd - Opened file descriptor to write to
59 | * @param data - Pointer to the data to write
60 | * @param size - Size of data to write
61 | *
62 | */
63 | int kuIoWrite(SceUID fd, const void *data, SceSize size);
64 |
65 |
66 | /**
67 | * Reposition read/write file descriptor offset
68 | *
69 | * @par Example:
70 | * @code
71 | * kuIoLseek(fd, -10, SEEK_END);
72 | * @endcode
73 | *
74 | * @param fd - Opened file descriptor with which to seek
75 | * @param offset - Relative offset from the start position given by whence
76 | * @param whence - Set to SEEK_SET to seek from the start of the file, SEEK_CUR
77 | * seek from the current position and SEEK_END to seek from the end.
78 | *
79 | */
80 | int kuIoLseek(SceUID fd, int offset, int whence);
81 |
82 | /**
83 | * Remove directory entry
84 | *
85 | * @param file - Path to the file to remove
86 | *
87 | */
88 | int kuIoRemove(const char *file);
89 |
90 | /**
91 | * Make a directory file
92 | *
93 | * @param dir
94 | * @param mode - Access mode.
95 | */
96 | int kuIoMkdir(const char *dir);
97 |
98 | /**
99 | * Remove a directory file
100 | *
101 | * @param path - Removes a directory file pointed by the string path
102 | */
103 | int kuIoRmdir(const char *path);
104 |
105 | /**
106 | * Return the position in the file
107 | *
108 | * @param fd - Opened file descriptor with which to seek
109 | * @param pos - The position in the file after the seek
110 | *
111 | */
112 | int kuIoTell(SceUID fd, SceOff* pos);
113 |
114 | #endif
--------------------------------------------------------------------------------
/kuio.yml:
--------------------------------------------------------------------------------
1 | kuio:
2 | attributes: 7
3 | version:
4 | major: 1
5 | minor: 1
6 | main:
7 | start: module_start
8 | modules:
9 | kuioLibrary:
10 | syscall: true
11 | functions:
12 | - kuIoOpen
13 | - kuIoWrite
14 | - kuIoRead
15 | - kuIoClose
16 | - kuIoLseek
17 | - kuIoMkdir
18 | - kuIoRmdir
19 | - kuIoRemove
20 | - kuIoTell
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "include/kuio.h"
11 |
12 | #define SAFE_MODE 1
13 |
14 | int kuioCpyPath(SceUID pid, char *dst, int max, const char *src){
15 |
16 | int res = 0x80010016;
17 | char path[0x400];
18 |
19 | if(pid == SCE_GUID_KERNEL_PROCESS_ID)
20 | goto end;
21 |
22 | SceSSize str_res = ksceKernelStrncpyFromUser(path, src, sizeof(path));
23 |
24 | if(str_res == 0 || str_res == sizeof(path) || strncmp(path, "sdstor", 6) == 0)
25 | goto end;
26 |
27 | res = ksceFiosKernelOverlayResolveSync(pid, 0, path, dst, max);
28 | if(res < 0)
29 | goto end;
30 |
31 | #ifdef SAFE_MODE
32 | if(strncmp(dst, "ux0:/data/", 10) != 0)
33 | res = 0x80010016;
34 | #endif
35 |
36 | end:
37 | return res;
38 | }
39 |
40 | int kuIoOpen(const char *file, int flags, SceUID *res){
41 |
42 | uint32_t state;
43 | int result;
44 | SceUID pid, fd_guid, fd_puid = 0x80010016;
45 | char path_resolved[0x400];
46 |
47 | ENTER_SYSCALL(state);
48 |
49 | pid = ksceKernelGetProcessId();
50 |
51 | result = kuioCpyPath(pid, path_resolved, sizeof(path_resolved), file);
52 | if(result >= 0){
53 | int curr_al = ksceKernelSetPermission(0x40);
54 | fd_guid = ksceIoOpen(path_resolved, flags, 6);
55 | ksceKernelSetPermission(curr_al);
56 |
57 | if(fd_guid >= 0){
58 | fd_puid = kscePUIDOpenByGUID(pid, fd_guid);
59 | result = 0;
60 | }else{
61 | fd_puid = fd_guid;
62 | result = fd_guid;
63 | }
64 | }
65 |
66 | ksceKernelMemcpyKernelToUser(res, &fd_puid, sizeof(*res));
67 | EXIT_SYSCALL(state);
68 | return result;
69 | }
70 |
71 | int kuIoClose(SceUID fd){
72 |
73 | int res = 0x80010016;
74 | uint32_t state;
75 | SceUID pid, fd_guid;
76 | ENTER_SYSCALL(state);
77 |
78 | int curr_al = ksceKernelSetPermission(0x40);
79 |
80 | pid = ksceKernelGetProcessId();
81 | if(pid == SCE_GUID_KERNEL_PROCESS_ID)
82 | goto end;
83 |
84 | fd_guid = kscePUIDtoGUID(pid, fd);
85 | if(fd_guid < 0){
86 | res = fd_guid;
87 | goto end;
88 | }
89 |
90 | res = ksceIoClose(fd_guid);
91 | if(res >= 0){
92 | res = kscePUIDClose(pid, fd);
93 | }
94 |
95 | end:
96 | ksceKernelSetPermission(curr_al);
97 | EXIT_SYSCALL(state);
98 | return res;
99 | }
100 |
101 | int kuIoRead(SceUID fd, void *data, SceSize size){
102 |
103 | uint32_t state;
104 | int res = 0x80010016;
105 | SceUID pid, fd_guid;
106 | void *kernel_page = NULL;
107 | SceSize kernel_size = 0;
108 | SceUInt32 kernel_offset = 0;
109 |
110 | ENTER_SYSCALL(state);
111 |
112 | int curr_al = ksceKernelSetPermission(0x40);
113 |
114 | pid = ksceKernelGetProcessId();
115 | if(pid == SCE_GUID_KERNEL_PROCESS_ID)
116 | goto end;
117 |
118 | fd_guid = kscePUIDtoGUID(pid, fd);
119 | if(fd_guid < 0){
120 | res = fd_guid;
121 | goto end;
122 | }
123 |
124 | int type = (ksceSblACMgrIsGameProgram(0) != 0) ? 0x11 : 1;
125 |
126 | SceUID mapid = ksceKernelMapUserBlock("KuioUserRefer", SCE_KERNEL_MEMORY_REF_PERM_USER_W, type, data, size, &kernel_page, &kernel_size, &kernel_offset);
127 | if(mapid < 0){
128 | res = mapid;
129 | goto end;
130 | }
131 |
132 | res = ksceIoRead(fd_guid, (void *)(((uintptr_t)kernel_page) + kernel_offset), size);
133 |
134 | ksceKernelMemBlockRelease(mapid);
135 |
136 | end:
137 | ksceKernelSetPermission(curr_al);
138 | EXIT_SYSCALL(state);
139 | return res;
140 | }
141 |
142 | int kuIoWrite(SceUID fd, const void *data, SceSize size){
143 |
144 | uint32_t state;
145 | int res = 0x80010016;
146 | SceUID pid, fd_guid;
147 | void *kernel_page = NULL;
148 | SceSize kernel_size = 0;
149 | SceUInt32 kernel_offset = 0;
150 |
151 | ENTER_SYSCALL(state);
152 |
153 | int curr_al = ksceKernelSetPermission(0x40);
154 |
155 | pid = ksceKernelGetProcessId();
156 | if(pid == SCE_GUID_KERNEL_PROCESS_ID)
157 | goto end;
158 |
159 | fd_guid = kscePUIDtoGUID(pid, fd);
160 | if(fd_guid < 0){
161 | res = fd_guid;
162 | goto end;
163 | }
164 |
165 | int type = (ksceSblACMgrIsGameProgram(0) != 0) ? 0x11 : 1;
166 |
167 | SceUID mapid = ksceKernelMapUserBlock("KuioUserRefer", SCE_KERNEL_MEMORY_REF_PERM_USER_R, type, data, size, &kernel_page, &kernel_size, &kernel_offset);
168 | if(mapid < 0){
169 | res = mapid;
170 | goto end;
171 | }
172 |
173 | res = ksceIoWrite(fd_guid, (const void *)(((uintptr_t)kernel_page) + kernel_offset), size);
174 |
175 | ksceKernelMemBlockRelease(mapid);
176 |
177 | end:
178 | ksceKernelSetPermission(curr_al);
179 | EXIT_SYSCALL(state);
180 | return res;
181 | }
182 |
183 | int kuIoLseek(SceUID fd, int offset, int whence){
184 |
185 | uint32_t state;
186 | int res = 0x80010016;
187 | SceUID pid, fd_guid;
188 |
189 | ENTER_SYSCALL(state);
190 |
191 | int curr_al = ksceKernelSetPermission(0x40);
192 |
193 | pid = ksceKernelGetProcessId();
194 | if(pid == SCE_GUID_KERNEL_PROCESS_ID)
195 | goto end;
196 |
197 | fd_guid = kscePUIDtoGUID(pid, fd);
198 | if(fd_guid < 0){
199 | res = fd_guid;
200 | goto end;
201 | }
202 |
203 | SceOff seek_res = ksceIoLseek(fd_guid, (int)offset, whence);
204 | if(seek_res < 0LL)
205 | res = (int)seek_res;
206 | else
207 | res = 0;
208 |
209 | end:
210 | ksceKernelSetPermission(curr_al);
211 | EXIT_SYSCALL(state);
212 | return res;
213 | }
214 |
215 | int kuIoTell(SceUID fd, SceOff *pos){
216 |
217 | uint32_t state;
218 | int res = 0x80010016;
219 | SceUID pid, fd_guid;
220 |
221 | ENTER_SYSCALL(state);
222 |
223 | int curr_al = ksceKernelSetPermission(0x40);
224 |
225 | pid = ksceKernelGetProcessId();
226 | if(pid == SCE_GUID_KERNEL_PROCESS_ID)
227 | goto end;
228 |
229 | fd_guid = kscePUIDtoGUID(pid, fd);
230 | if(fd_guid < 0){
231 | res = fd_guid;
232 | goto end;
233 | }
234 |
235 | SceOff seek_res = ksceIoLseek(fd_guid, 0, SCE_SEEK_CUR);
236 | if(seek_res < 0LL){
237 | res = (int)seek_res;
238 | seek_res = 0LL;
239 | }else{
240 | res = 0;
241 | }
242 |
243 | ksceKernelMemcpyToUser(pos, &seek_res, sizeof(*pos));
244 |
245 | end:
246 | ksceKernelSetPermission(curr_al);
247 | EXIT_SYSCALL(state);
248 | return res;
249 | }
250 |
251 | int kuIoMkdir(const char* dir){
252 |
253 | uint32_t state;
254 | int res;
255 | char path_resolved[0x400];
256 |
257 | ENTER_SYSCALL(state);
258 |
259 | res = kuioCpyPath(ksceKernelGetProcessId(), path_resolved, sizeof(path_resolved), dir);
260 | if(res >= 0){
261 | int curr_al = ksceKernelSetPermission(0x40);
262 | res = ksceIoMkdir(path_resolved, 6);
263 | ksceKernelSetPermission(curr_al);
264 | }
265 |
266 | EXIT_SYSCALL(state);
267 | return res;
268 | }
269 |
270 | int kuIoRemove(const char *file){
271 |
272 | uint32_t state;
273 | int res;
274 | char path_resolved[0x400];
275 |
276 | ENTER_SYSCALL(state);
277 |
278 | res = kuioCpyPath(ksceKernelGetProcessId(), path_resolved, sizeof(path_resolved), file);
279 | if(res >= 0){
280 | int curr_al = ksceKernelSetPermission(0x40);
281 | res = ksceIoRemove(path_resolved);
282 | ksceKernelSetPermission(curr_al);
283 | }
284 |
285 | EXIT_SYSCALL(state);
286 | return res;
287 | }
288 |
289 | int kuIoRmdir(const char *dir){
290 |
291 | uint32_t state;
292 | int res;
293 | char path_resolved[0x400];
294 |
295 | ENTER_SYSCALL(state);
296 |
297 | res = kuioCpyPath(ksceKernelGetProcessId(), path_resolved, sizeof(path_resolved), dir);
298 | if(res >= 0){
299 | int curr_al = ksceKernelSetPermission(0x40);
300 | res = ksceIoRmdir(path_resolved);
301 | ksceKernelSetPermission(curr_al);
302 | }
303 |
304 | EXIT_SYSCALL(state);
305 | return res;
306 | }
307 |
308 | void _start() __attribute__ ((weak, alias ("module_start")));
309 | int module_start(SceSize argc, const void *args) {
310 | return SCE_KERNEL_START_SUCCESS;
311 | }
312 |
--------------------------------------------------------------------------------