Hola.
Supongo que mas o menos, tenéis una idea vaga de lo que es un custom IOS. Un custom IOS (o cIOS para abreviar) no es mas que una modificación o añadido que se le hace a los IOS originales para conseguir nuevas prestaciones y por ejemplo, poder emular la unidad de DVD desde un disco duro para cargar un backup, pero también puede servir para abrir una puerta nueva al Homebrew y con esa idea diseñé mload.
mload es un módulo (un programa) como cualquier otro de los que componen el IOS, orientado como una herramienta que suministrara una forma de cargar otros módulos externos desde el PPC, hacia el Starlet y así permitir que las aplicaciones pudieran proporcionar de forma personalizada, sus propios módulos, sin necesidad de tener que instalar un nuevo cIOS cada vez que se lleve a cabo una modificación de cualquier tipo. De esta forma, se facilita el desarrollo y se evitan colisiones e incompatibilidades indeseadas por que el cIOS está preparado para trabajar de forma diferente.
Trabajar en el Starlet no es sencillo, puesto que por un lado, hay que convivir con el resto de módulos que componen el IOS, respetando sus direcciones de memoria, etc, pero también hay una serie de protecciones de memoria y permisos que hacen que por ejemplo, si desde un módulo trato de escribir en determinadas áreas, se produzca un cuelgue (por una excepción). Además, nosotros no sabemos casi nada y tenemos que avanzar a tientas probando cosas.
Así pues, cuando desarrollé mload lo primero que tuve que buscar fue una zona de memoria que estuviera libre y que proporcionara un espacio lo suficientemente amplio para que pudiéramos cargar nuestros módulos: la zona elegida fue la comprendida entre 0x13700000 y 0x1377ffff. Es decir, 512KB para nuestros propósitos, que es un espacio aceptable y que parece que no da problemas. También tuve que investigar el uso de las syscalls y aprender a ejecutar hilos y controlarlos, así como la utilización de los timers, etc (cosa de la que también hablaré mas adelante)
Características generales de mload
mload se compone de tres secciones de memoria. El código ejecutable se carga en la dirección 0x138c0000, el espacio de variables, la pila etc, a partir 0x138c8000 y el área que nos interesa, en 0x13700000, ajustando los permisos como lectura y escritura, simplemente (lo podéis ver en la carpeta de scripts en el fichero link.ld de los fuentes del módulo).
Cuando compilamos el programa, obtenemos un módulo con unas dimensiones un tanto respetables (unos 643 KB!), pero éste módulo elf no está preparado para que el IOS lo pueda asimilar tal cual y debe pasar por una herramienta llamada stripios (de nuevo os remito a los fuentes) que yo modifiqué para que pudiera comprimir una de las secciones que le indiquemos, a un tamaño de 1 solo byte (por tratarse de un espacio vacío en realidad) utilizando uno de los trucos del formato elf .
De esa forma, la sección 0x13700000 reduce todo su tamaño (en el makefile podéis ver la línea "@$(STRIPIOS) $< $@ strip 0x13700000" que se ocupa de ello) y el módulo a incluir en el IOS es de solo 7KB como resultado (esto si que es "majia" de la buena ).
Desde el PPC (también desde el Starlet obviamente), podemos acceder a una serie de funciones para manipular la memoria o ejecutar módulos desde el dispositivo "dev/mload" (que luego trataremos con más detalle), principalmente, en el área de trabajo entre 0x13700000 y 0x1377ffff, claro está.
En la última versión de mload se suministra también una serie de funciones nuevas, cuyo propósito principal es ganar permisos a la
hora de realizar ciertas tareas, mediante Interrupciones por Software (SWI) desde el Starlet y de esa forma poder realizar casi cualquier cosa (por ejemplo, yo utilizo estas herramientas para cambiar el vector original de interrupciones y procesarlo desde el módulo EHCI o copiar zonas de memoria críticas, con las interrupciones deshabilitadas y todos los permisos).
Mload responde al servicio "svc 0xcc", permitiendo registrar otros servicios desde 0x00 a 0xff (exceptuando 0xcc obviamente) y tratando de forma interna el servicio "svc 0xab", en concreto la función os_puts(""); para obtener logs reportados por otros módulos (obviamente, se puede registrar otra función para tratar 0xab y redirigir los mensajes a otro dispositivo)
Otro detalle interesante, es que mload es cargado usando la máxima prioridad admitida (0x79) con el fin de permitir que podamos crear hilos con el abanico más amplio de posibilidades (un hilo no puede crear otro hilo que tenga mayor prioridad que el).
Mload identifica el IOS base (entre 36, 37,38 y 60) y hace los parches necesarios de forma directa e interna, aunque por el momento, solo ha podido cargarse con éxito con base 36 y 38. También se ocupa de pre-inicializar el driver EHCI.
Preparación del cIOS: parches y módulos
El instalador actual, realiza dos series de parches: uno destinado al modulo que yo suelo denominar "ES" de forma genérica para identificarlo (aunque en realidad contiene más cosas, como un cargador de elf con distintas secciones con el kernel, etc). Y otro que va destinado al modulo "DIP". En cIOS 202 se capan los parches destinados a facilitar la conexión de dip_plugin (código en formato binario que es cargado en la dirección 0x1377E000 que hace de interfaz entre el módulo DIP y el módulo EHCI para la emulación de DVD)
Los parches de la parte ES se han transferido al módulo mload y se han reemplazado en el instalador por dos nuevos parches que actúan sobre la tabla de saltos de la syscall os_software_IRQ (función que se usa para habilitar una interrupción ligada a un dispositivo), en concreto para os_software_IRQ(4) (para poder habilitar las interrupciones EHCI, saltando una comprobación) y para os_software_IRQ(9) (interrupción fuera de uso en el Starlet y que aquí se utiliza para saltar al crt0.s de mload y ganar permisos de sistema, con los que poder llevar a cabo los parches necesarios desde mload). A efectos prácticos, una sola llamada a os_software_IRQ(9) durante la ejecución de mload, es lo que hace que mload se vuelva especial y tengamos un gran poder en nuestras manos.
El módulo EHCI, lo compilo para ser cargado en la dirección 0x13700000 hasta la 0x1373ffff (ocupa la mitad del espacio disponible!) pero también hay que decir que en realidad se compone de tres módulos en uno (EHCI+USB Storage+WBFS) y soy generoso proporcionándole espacio para la pila y asignación de memoria .
Tanto el módulo EHCI (ehcmodule.elf) como dip_plugin, no son cargados por el cIOS, si no que son módulos externos que se alojan en memoria por las aplicaciones (por ejemplo, uLoader) y cada cual puede repartir a su gusto la memoria como le parezca (cargando otro tipo de módulos o modificando sus direcciones de linkado)
Caja de Herramientas de dev/mload
Para trabajar desde el PPC con mload, en examples/ppc/libmload podeis encontrar un conjunto de herramientas con las que realizar diferentes tareas.
En mload.h podéis ver el catálogo de las funciones disponibles. Todas las funciones, llaman a mload_init() de forma interna y retornan un valor negativo en caso de error. El único cuidado especial que debemos de tener, es de llamar a mload_close(); antes de cargar un nuevo IOS si vamos a seguir utilizando los servicios de dev/mload.
Aquí se listan las principales funciones:
IOSint mload_get_IOS_base();
ret: devuelve el IOS utilizado como base del cIOS (FFS, ES, IOSP), actualmente el 38. Esto es importante conocerlo porque por ejemplo, si vamos a cambiar el vector de interrupciones, realizar parches de algún tipo o incluso llamar funciones del sistema desde fuera, las localizaciones son diferentes en los distintos IOS
Carga de módulosint mload_module(void *addr, int len);
Función pensada para cargar módulos pequeños desde la memoria del PPC de forma directa. Recomiendo utilizar mejor mload_elf()
addr: dirección del módulo .elf en memoria, alineada a 32
len: longitud del módulo elf
ret: devuelve negativo si hubo error o la id del hilo creado en el Starlet.int mload_elf(void *my_elf, data_elf *data_elf);
Función utilizada para copiar en la memoria del Starlet las diferentes secciones de un módulo en formato elf.
my_elf: dirección del módulo .elf en memoria, alineada a 32
data_elf: estructura que es inicializada por mload elf situando los valores de inicio, prioridad, stack, etc
ret: 0->OK <0 -> Errorint mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority);
Función utilizada para ejecutar un hilo en el starlet (lo normal es utilizarla en conjunción con los datos devueltos por mload_elf() en data_elf)
starlet_addr: dirección de inicio del hilo del programa
starlet_top_stack: dirección superior de la pila (la pila es descendente).
stack_size: tamaño total de la pila.
priority: prioridad de 0x1 a 0x79 (maxima)
ret: devuelve negativo si hubo error o la id del hilo creado en el Starlet.
Leer/Escribir memoriaint mload_seek(int offset, int mode);
Posicionar el puntero en memoria para las operaciones de lectura/escritura
offset: dirección de memoria absoluta/relativa en el Starlet
mode: SEEK_SET normalmente.
ret: <0 errorint mload_read(void* buf, u32 size);
int mload_write(const void * buf, u32 size);
lectura/escritura desde la posición del puntero en el Starlet
buf: buffer donde se leerá o desde el que se escribirá. Se recomienda alinear a 32
size: bytes a leer o escribir
ret: <0 error. Numero de bytes leidos/escritos.int mload_memset(void *starlet_addr, int set, int len);
función especial que equivale a memset() para el Starlet
Especialesvoid * mload_get_ehci_data();
Devuelve la dirección de la estructura EHCI. Se utiliza en ehcmodule.elf.int mload_get_log();
Función que situa el puntero de lectura/escritura en la dirección de inicio del área reservada para logs mediante la función os_puts(). Normalmente, tiene una extensión de 4KB y se compone de una cadena de caracteres terminada en '\0'.
Debería ir seguida de un mload_read() y tiene su utilidad para volcar su contenido a la SD, por ejemplo.
ret: si falla retorna <0. Devuelve el tamaño máximo del buffer de log.
int mload_getw(const void * addr, u32 *dat);
int mload_geth(const void * addr, u16 *dat);
int mload_getb(const void * addr, u8 *dat);
int mload_setw(const void * addr, u32 dat);
int mload_seth(const void * addr, u16 dat);
int mload_setb(const void * addr, u8 dat);
Funciones especiales para leer/escribir registros o zonas de memoria sin cachear accesibles en modo usuario desde el Starlet.
Syscalls
Bien, ya conocemos las funciones de mload que nos permiten leer/escribir/cargar y ejecutar en memoria, código o modulos desde el PPC, pero de poco nos sirve eso si no conocemos una serie de funciones que nos proporciona el sistema para construir dichos módulos y poder comunicarnos con ellos. Así que creo que vendría bien darle un repaso a las principales syscalls que podemos necesitar para trabajar con nuestros módulos.
Antes de nada, me gustaría señalar que no podemos utilizar ciertas funciones de la librería estándar como malloc(), free() o printf().
Memoriaint os_heap_create(void* ptr, int size);
Función que se utiliza para crear un montículo de memoria que utilizaremos en nuestro módulo de forma privada. Dicho de otra forma, es como si indicáramos en un programa una zona de memoria que luego se asignará con malloc() (que ya he mencionado que no se debe utilizar)
ptr: inicio del area de memoria utilizada como montículo (alineado a 32, probablemente)
size: tamaño en bytes del montículo
ret= <0 error. Id del Heapvoid* os_heap_alloc(int heap, unsigned int size);
El equivalente al malloc().
heap: id del heap a utilizar. 0 parece ser el montículo global
size: tamaño en bytes asignar.
ret= dirección de la memoria asignada o NULLvoid os_heap_free(int heap, void* ptr);
El equivalente a free().
heap -> id del heap.
ptr ->dirección a liberar
Caché
Como sabéis, los procesadores implementan memoria caché para acelerar el acceso de memoria, dado que la memoria RAM es más lenta, tanto para el código en ejecución (icache) como para datos (dcache). Las syscalls siguientes se utilizan para trabajar con la caché de datos.void os_sync_before_read(void* ptr, int size);
Función a utilizar antes de las lecturas del procesador cuando se presume que han habido operaciones DMA (fuera de la caché) en el bloque de memoria especificado. Tengo una duda, sobre ésta función, que no he comprobado : no se si simplemente, invalida la caché o si además procede a refrescarla (si es el primer caso, las líneas de caché se irían refrescando a medida que fuéramos leyendo desde la RAM, por lo que si invalidamos la caché y al cabo de un segundo, por ejemplo, cambiáramos un dato vía DMA y después procediéramos a leer dicho dato, tendríamos el dato mas actual, porque la caché se refrescaría en el momento de leer, mientras que en el segundo caso, tendríamos el dato antiguo, puesto que la caché se habría refrescado cuando llamamos a os_sync_before_read() y la lectura se haría dentro de la cache (salvo que por alguna razón externa, el procesador hubiera descartado esos datos) y no serían visibles los cambios fuera de caché, salvo que llamáramos de nuevo a os_sync_before_read(). Solo se que el ARM tiene una instrucción específica para invalidar caché, pero aquí hay otras operaciones un tanto raras que sugieren que puede haber una invalidación y refresco de la caché.
ptr: dirección de memoria. Ojo con esto porque las lineas de caché trabajan con 32 bytes, por lo que si se solapan datos (es decir, que el puntero no esté alineado a 32), se puede producir resultados imprevistos.
size: tamaño en bytes. Ojo con esto por que el tamaño es redondeado para ser divisible entre 32 y si se solapan datos (es decir, que el redondeo exceda del tamaño) se pueden producir resultados imprevistosvoid os_sync_after_write(void* ptr, int size);
Función a utilizar después de las escrituras cuando se presume que se va a utilizar el bloque de memoria especificado en operaciones DMA (fuera de la caché), para que la caché se escriba en la memoria RAM.
ptr: dirección de memoria. Ojo con esto porque las lineas de caché trabajan con 32 bytes, por lo que si se solapan datos (es decir, que el puntero no esté alineado a 32), se puede producir resultados imprevistos.
size: tamaño en bytes. Ojo con esto por que el tamaño es redondeado para ser divisible entre 32 y si se solapan datos (es decir, que el redondeo exceda del tamaño) se pueden producir resultados imprevistos
Colas
Las colas (queue) son el mecanismo que nos permite sincronizar la ejecución de los distintos hilos, de forma que podemos registrar una cola para un dispositivo por ejemplo y ésta queda a la espera de que se le envíe un mensaje, que puede ser la dirección de una estructura de datos o simplemente una señal de otro tipo.int os_message_queue_create(void* ptr, unsigned int id);
Función para crear una cola.
ptr: puntero donde se recibirán los mensajes de la cola.
id: Numero de entradas que soportará la cola. Cada entrada ocupa 4 bytes en la memoria señalada por ptr. Hay que tener mucho cuidado de no envioar mas mensajes de los que la cola pueda soportar (especialmente peligroso cuando trabajemos con timers). Por cierto, la etiqueta 'id' debería ser un 'max_entries' o algo similar.
ret: <0 si error. queuehandle asociado a la cola.int os_message_queue_receive(int queue, unsigned int* message, unsigned int flags);
Función que aguarda a que una cola reciba un mensaje, interrumpiendo la ejecución del hilo.
queue: queuehandle (devuelto por os_message_queue_create())
message: Si es un dispositivo, lo normal es que sea la dirección de una estructura ipcmessage (ver syscalls.h)
flags: siempre a 0
ret: <0 error. Es posible que devuelva el numero de elementos en cola, pero no lo he comprobado.int os_message_queue_send(int queue, unsigned int message, int flags);
int os_message_queue_send_now(int queue, unsigned int message, int flags);
Función para enviar un mensaje a una cola. Probablemente el send_now provoque la activación del hilo saltándose la prioridad de las colas.
queue: queuehandle (devuelto por os_message_queue_create())
message: dirección o mensaje a enviar a la cola.
flags: a 0
ret: <0 errorvoid os_message_queue_ack(void* message, int result);
Función que devuelve una respuesta del dispositivo mediante IPC. Realmente, no veo que relación tiene esto con la colas por que
es más bien una función de respuesta de dispositivo, que otra cosa ...
message: Dirección de la estructura ipcmessage que se recibió mediante os_message_queue_receive()
result: Resultado de la operación IPC solicitada (IOS_OPEN, IOS_CLOSE, ...)
Dispositivosint os_device_register(const char* devicename, int queuehandle);
Función que asocia un nombre de dispositivo a una cola, para operaciones IPC.
devicename: Nombre del dispositivo: Ej "/dev/mload"
queuehandle: queuehandle (devuelto por os_message_queue_create())int os_open(char* device, int mode);
Equivalente a IOS_Open en PPC.
device: nombre del dispositivo a abrir.
mode: flags de modo (O_CREAT, O_TRUNC, O_RDONLY, O_WRONLY, O_RDWR ... en include/sys/fcntl.h)
ret: <0 error, fd del dispositivo.int os_close(int fd);
Cierra el dispositivo.
fd: fd del dispositivo
ret: <0 errorint os_seek(int fd, int offset, int mode);
Posiciona el puntero del dispositivo.
fd: fd del dispositivo
offset: posición dentro del dispositivo
mode: SEEK_SET, SEEK_END, SEEK_CUR...
ret: <0 error. Posición actual absoluta dentro del dispositivoint os_read(int fd, void *d, int len);
int os_write(int fd, void *s, int len);
Funciones para lectura/escritura en dispositivo
fd: fd del dispositivo
s: d: direcciones fuente/destino para escritura/lectura
len: longitud en bytes.
ret: <0 Error. Numero de bytes escritos/leídos.int os_ioctlv(int fd, int request, int bytes_in, int bytes_out, ioctlv *vector);
Función de control I/O mediante vectores. Conviene utilizar las funciones de caché apropiadas en las transferencias de datos.
fd: fd del dispositivo
request: indice función ioctlv
bytes_in: Numero de elementos de entrada (la etiqueta está mal, puesto que no son bytes)
bytes_out: Numero de elementos de salida (la etiqueta está mal, puesto que no son bytes)
vector: Dirección del array de vectores (tipo ioctlv en syscalls.h). in+out
ret: <0 error. Resultado depende de la función seleccionada.int os_ioctl(int fd, int request, void *in, int bytes_in, void *out, int bytes_out);
Función de control I/O. Conviene utilizar las funciones de caché apropiadas en las transferencias de datos.
fd: fd del dispositivo
request: indice función ioctl
in: dirección buffer de entrada
bytes_in: longitud buffer de entrada
out: dirección buffer de salida
bytes_out: longitud buffer de salida
ret: <0 error. Resultado depende de la función seleccionada.
Timers
Los timers se utilizan para enviar un mensaje a la cola cada cierto tiempo. Internamente, son controlados por una alarma que genera una interrupción cada cierto tiempo, por lo que el si el tiempo del timer es muy bajo, seguramente tenga cierta distorsión (aparte de que la activación de las colas dependerá de la prioridad de los hilos, etc). La resolución de los timers se mide en microsegundos. Hay que tener especial cuidado de que el tiempo de repetición no sea demasiado corto y acabe por rebasar la cola asociada.int os_create_timer(int time_us, int repeat_time_us, int message_queue, int message);
Función para crear un timer.
time_us: tiempo en microsegundos de la primera cuenta
repeat_time: tiempo en microsegundos de las siguientes cuentas. Si solo se requiere una cuenta, se puede fijar un tiempo muy alto aquí y proceder a parar el timer cuando se reciba el mensaje
message_queue: queuehandle de la cola que recibirá el mensaje
message: mensaje que recibirá la cola al cumplirse el tiempo (podría ser la dirección de una estructura, por ejemplo)
ret: <0 error al crear el timer. id del timer handle asociadoint os_stop_timer(int timer_id);
Para la cuenta de un timer. Se puede restablecer con os_restart_timer().
timer_id: Timer handle
ret: <0 errorint os_destroy_timer(int time_id);
Elimina un timer. Es posible que sea necesario pararlo primero con os_stop_timer().
timer_id: Timer handle
ret: <0 errorint os_restart_timer(int timer_id, int time_us);
Reinicia un timer que fue parado con os_stop_timer(). La cuenta es repetitiva, por lo que hay que tener cuidado que el tiempo no sea demasiado corto como para que se produzca un desborde en la cola asociada al timer.
timer_id: Timer handle
time_us: tiempo en microsegundos de las sucesivas repeticiones
ret: <0 errorint os_timer_now(int time_id);
Probablemente fuerce a un timer a enviar el mensaje y reiniciar su cuenta al ser llamada
timer_id: Timer handle
ret: <0 error
Interrupcionesint os_register_event_handler(int device, int queue, int message);
Registra una cola a un dispositivo que recibirá eventos mediante interrupciones. Solo es posible registrar si no hay otro evento registrado.
device: IRQ del dispositivo en cuestión 4->EHCI, 5->OHC0, etc (ver libcios/include/starlet.h)
queue: quehandle de la cola asociada
message: mensaje que se envía a la cola
ret: <0 si errorint os_unregister_event_handler(int device);
Elimina los eventos por interrupción para el dispositivo especificado.
device: IRQ del dispositivo en cuestión 4->EHCI, 5->OHC0, etc (ver libcios/include/starlet.h)
ret: <0 si errorint os_software_IRQ(int dev);
Función que habilita el uso de interrupciones en el dispositivo. Tiene una doble función: por un lado, elimina una posible interrupción pendiente para dicho dispositivo y por otro, ajusta la máscara de interrupciones (Hollywood) para que estas puedan producirse. Sin embargo, ésta syscall solo admite fijar desde IRQ4 (EHCI) en adelante y existe una comprobación (probablemente ligada a la ID del proceso) que hace que no en todos los casos se pueda fijarla interupción desde aquí (de ahí que uno de los parches de mload se dedique a habilitar os_software_IRQ(4).
Sin embargo, es posible acceder a los registros HW_ARMIRQFLAG y HW_ARMIRQMASK de forma directa mediante los servicios SWI de mload (como veremos mas adelante)
dev: IRQ del dispositivo en cuestión a partir de 4->EHCI, 5->OHC0, etc (ver libcios/include/starlet.h)
ret: <0 error
Hilos
NOTA: el thread ID=0 indica hilo actual en algunas funciones.int os_thread_create( unsigned int (*entry)(void* arg), void* arg, void* stack, unsigned int stacksize, unsigned int priority, int autostart);
Función que crea un hilo de programa en el starlet. El hilo se encuentra parado de inicio y requiere el uso de os_thread_continue() para ejecutarse.
entry: función de inicio que recibirá el foco del hilo.
arg: argumento que se le pasará a la función de inicio
stack: dirección de memoria como tope de pila. Por ejemplo, si dedicamos u32 my_stack[STACK_SIZE] como pila del hilo, aquí habria que pasar &my_stack[STACK_SIZE] como dirección
stacksize: Tamaño en bytes de la pila
priority: prioridad entre 0x1 y 0x79. La prioridad no puede ser mayor que la del hilo donde se invoca ésta función (o no funcionará)
autostart: a 0. No parece funcionar como autostart y puede que no tenga uso o que sirva para indicar otra cosa.
ret: <0 Error. ID del hilo.int os_thread_continue(int id);
Continua con la ejecución del hilo (después de crearse o de una parada)
id: ID del hilo
ret: <0 errorint os_thread_stop(int id);
Para la ejecución de un hilo.
id: ID del hilo
ret: <0 errorint os_get_thread_id(void);
Devuelve la ID del hilo actual.
ret: ID del hilo.void os_thread_set_priority(int id, unsigned int priority);
Fija la prioridad actual del hilo.
id: ID del hilo
priority: prioridad entre 0x1 y 0x79. La función fallará si la prioridad es mayor que la de la creación del hilo.int os_thread_get_priority(void);
Devuelve la prioridad del hilo actual.
ret: <0 error. Prioridad actual
Logvoid os_puts(char *str);
Función que envía una cadena de texto al buffer de log
Códigos Fuentes
Antes de mirar algunos ejemplo de código, me gustaría explicar un poco la organización de los fuentes de mload, para que sepais donde buscar información:apps
|--- cios_installer -> Instalador de los cIOS 202/222/223
cios_mload
|
|------- cios_installer -> Fuentes del instalador de cIOS
|
|------- ehcmodule -> Fuentes del módulo EHCI
|
|------- examples -> Ejemplos para PPC y Starlet. Librería mload para PPC
| |
| |---- ppc
| | |------ example1 -> Ejemplo simple que muestra la actividad de un módulo
| | |------ example2 -> Ejemplo que muestra la utilización del módulo FAT/FAT32
| | |------ libmload -> Librería utilizada en el apartado "Caja de Herramientas de mload"
| |
| |---- starlet
| | |------ example1 -> módulo que muestra el uso de timers, colas y multihilo (para resetear una cuenta)
| | |------ libfat -> módulo que soporta FAT/FAT32 con memorias SD
| |
| |---- stripios -> réplica de la utilidad stripios
|
|------- libcios -> Librerías base
| |
| |------ include
| | |------- starlet.h -> Información sobre registros (Hollywood) IRQ, GPIO, TIMER (sacado de wiibrew.org)
| | |------- swi_mload.h -> Librería de servicios SWI de mload (Avanzado)
| | |------- syscalls.h -> Librería de syscalls
| | |------- types.h -> tipos de datos usados en el Starlet
| |
| |------ source
| |------- swi_mload.c -> Librería de servicios SWI de mload (Avanzado)
| |------- swi_mload.s -> Código ensamblador para llamar SWI
| |------- syscalls.s -> Código ensamblador para llamar a las syscalls
|
|------- mload -> Fuentes del módulo mload
|
|------- stripios -> Utilidad que convierte los elfs normales a los que precisa el IOS
|
|------- tinyehci -> Fuentes del driver EHCI /USB Storage
|
|------- wii_wbfs -> Fuentes para trabajar con particiones WBFS
Organización de un módulo
Los módulos en el Starlet, se compilan en una dirección fija y además es necesario convertir el elf resultante mediante la utilidad stripios. Voy a explicar aquí como están estructurados los fuentes partiendo del módulo example1:example1
|
|------- bin -> Aquí se alojará el módulo resultante
|------- scripts -> Aquí se encuentra nostart.specs y link.ld. Este último contiene el mapa de direcciones del módulo
|------- source
| |---- crt0.S -> Aquí se fija la prioridad inicial del módulo, la pila, etc. Y otras rutinas en ensamblador.
| |---- main.c -> el cuerpo del módulo propiamente dicho
|
|------- Makefile
Creando un hilo y un timer que lo active
En primer lugar, debemos declarar un espacio que utilizaremos para la pila:#define THREAD_STACK 1024
u8 thread_stack[THREAD_STACK] __attribute__ ((aligned (32)));
Luego, debemos declarar una función que recibirá el foco del hilo:int thread_start(void *arg)
{
/* creamos una cola que usaremos en el hilo para aguardar un evento que lo active
observar que en este caso, estamos utilizando como heaphandle 0, para asignar 32 bytes y una cola de 8 elementos (de 4 bytes)
*/
int thread_queuehandle = os_message_queue_create( os_heap_alloc(0, 0x20), 8);
/* vamos a definir un evento (en este caso un timer) que enviará un mensaje que activará el hilo cada 10 segundos */
os_create_timer(1000*1000*10, 1000*1000*10, thread_queuehandle, 0x555);
while(1)
{
u32 message;
/* aguarda a que la cola reciba un mensaje */
os_message_queue_receive(thread_queuehandle, (void*)&message, 0);
if(message==0x555)
{
// mensaje del timer recibido: hacer aquí lo que corresponda
}
}
return 0;
}
Ahora tocaría crear el hilo desde el main():int my_thread_id=os_thread_create( (void *) thread_start, NULL, &thread_stack[THREAD_STACK], THREAD_STACK, 0x48, 0);
if(my_thread_id>=0) os_thread_continue(my_thread_id);
Y con este ejemplo, doy por finalizada la información básica. Tenéis suficiente código tanto en mload, ehcmodule como en los ejemplos, para ver otros usos y no tiene sentido que me extienda más sobre éste tema. En el siguiente post que tengo reservado, casi a continuación, trataré de explicar las funciones SWI de mload y algunos usos de forma mas avanzada.
Descarga de fuentes de mload
http://mods.elotrolado.net/~hermes/wii/ ... ll_3.5.rar
OFFSET 0xFFFF0000 Reset (Modo Supervisor)
OFFSET 0xFFFF0004 Instrucción Indefinida (Modo Indefinido). Las syscalls de Wii que conocemos
OFFSET 0xFFFF0008 Interrupción de Software SWI (Modo Supervisor): Salta a una instrucción de retorno en los IOS, excepto en mload
OFFSET 0xFFFF000C Abort (Prefech) (Modo Abort)
OFFSET 0xFFFF0010 Abort (Data) (Modo Abort)
OFFSET 0xFFFF0014 Reserved
OFFSET 0xFFFF0018 IRQ: Interrupciones (Modo IRQ)
OFFSET 0xFFFF001C FIQ: Interrupciones rápidas (Modo FIQ). En Wii sin uso
void swi_mload_add_handler(u8 svc_code, int (*func) (u32 arg0, u32 arg1, u32 arg2, u32 arg3));
Función que permite añadir un handler para tratar las diferentes funciones "svc x", donde x puede se cualquier valor entre 0x0 y 0xff, excepto 0xcc que siempre estará controlado por mload. La razón de ésta función es permitir que otros módulos puedan añadir su propio vector para intercambiar datos, por ejemplo. La función "svc 0xab" se encuentra registrada internamente para redirigir os_puts(), pero puede ser cambiada gracias a ésta función, para ser tratada desde otro lugar.
svc_code: desde 0x0 a 0xff exceptuando 0xcc. Por ejemplo 0xab para "svc 0xab"
func: función que tratará el nuevo servicio SWI. La función recibe los argumentos como registros, desde el r0 al r3 y se ejecutará en Modo Supervisor, por lo que acceder a otros argumentos extras, sería algo complicado (las funciones de C reciben los 4 primeros argumentos en los registros r0 a r3, y el resto en el stack). El valor de retorno es retornado como en cualquier otra función de C (registro r0)
ret: no retorna nada
void * swi_mload_EHCI_data(void);
Esta función retorna la estructura EHCI pre-inicializada desde mload (es lo mismo que se obtenía utilizando la función equivalente en dev/mload)
Su uso se limita a ehcmodule.elf
u32 swi_mload_get_syscall_base(void);
Obtiene la dirección de la tabla de las funciones de syscall. A partir de ésta dirección, podemos llamar a algunas syscalls de forma directa.
Por ejemplo, si queremos llamar a os_sync_before_read(), debemos llamar a la syscall 0x3f. Partiendo de la dirección que devuelve
ésta función, podemos calcular que: syscall_base+ 0x3f*4, es la dirección de la función que necesitamos.
Mas adelante, describiré éste acceso directo de forma practica, aunque podéis observarlo tanto en el crt0.s y el main de ehcmodule, como en los fuentes de mload.
ret: Dirección de la tabla de funciones de las syscalls
u32 swi_mload_get_ios_base(void);
Retorna la versión usada como IOS base (actualmente 38). Puede detectar IOS 36,37,38 y 60. Es útil para poder adaptar los parches que requiramos de forma dinámica (por ejemplo, ehcmodule utiliza ésta función para adaptar la rutina que tratará el vector de interrupción, para cada IOS)
ret: IOS base.
void swi_mload_memcpy(void * dst, void * src, int len);
Función que copia un bloque de memoria desde la dirección fuente a la dirección destino
Lo que la hace especial es:
-1) La copia se hace desde el Modo Supervisor y con las interrupciones deshabilitadas
-2) Se guarda los permisos de acceso y se establecen como Manager, por lo que no se producirán fallos de acceso
-3) Función memcpy() que copia el bloque de memoria
-4) Llamada directa a la función de la syscall os_sync_after_write() para que los datos escritosse copien de la caché a la memoria RAM
-5) Restablece los permisos de acceso originales
En la práctica equivale a copiar un bloque de memoria dentro de la caché a otro punto sin que se produzcan interrupciones, ni fallos de acceso (por falta de permisos) y sincronizando los datos de la caché en escritura con la memoria RAM (para poder utilizarlos con una DMA, por ejemplo)
void swi_mload_memcpy_from_uncached(void * dst, void * src, int len);
Función que copia un bloque de memoria desde la dirección fuente a la dirección destino, invalidando primero, los datos de la caché desde la dirección fuente
Lo que la hace especial es:
-1) La copia se hace desde el Modo Supervisor y con las interrupciones deshabilitadas
-2) Se guarda los permisos de acceso y se establecen como Manager, por lo que no se producirán fallos de acceso
-3) Llamada directa a la función de la syscall os_sync_before_read() para que los datos a leer se actualicen desde la memoria RAM a la caché
-4) Función memcpy() que copia el bloque de memoria
-5) Llamada directa a la función de la syscall os_sync_after_write() para que los datos escritos se copien de la caché a la memoria RAM
-6) Restablece los permisos de acceso originales
En la práctica equivale a copiar un bloque de memoria desde fuera de la caché (datos procedentes de una DMA, por ejemplo) a otro punto sin que se produzcan interrupciones, ni fallos de acceso (por falta de permisos) y sincronizando los datos de la caché en escritura con la memoria RAM.
u32 swi_mload_get_register(u32 addr);
Lectura de un registro de 32 bits
addr: dirección del registro
ret: valor del registro
void swi_mload_put_register(u32 addr, u32 val);
Escritura de un registro de 32 bits
addr: dirección del registro
val: valor a escribir
ret: No devuelve nada
void swi_mload_set_register(u32 addr, u32 val);
Fija bits en un registro de 32 bits. Equivale a *(addr)|=val; Los bits a 1 se fijan
addr: dirección del registro
val: valor con los bits a fijar
ret: No devuelve nada
void swi_mload_clr_register(u32 addr, u32 val);
Borrar bits en un registro de 32 bits. Equivale a *(addr)&=~val. Los bits a 1 se borran
addr: dirección del registro
val: valor con los bits a borrar
ret: No devuelve nada
int swi_mload_call_func(int (*func) (void *in, void *out), void *in, void *out);
Función que se utiliza para invocar a otra función que proporcione el usuario, que será llamada en Modo Supervisor, con las Interrupciones desconectadas. Es muy importante que las interrupciones permanezcan deshabilitadas dentro de dicha función (no se debe modificar los flags del registro CPSR. Gracias a ésta función, se hace innecesario ampliar los servicios SWI de mload en el futuro. Se dispone de una pila con 0x900 bytes, así que mucho cuidado con el tamaño de las variables locales. Tambien se recomienda que las funciones sean relativamente rápidas, para no interferir con la respuesta a las interrupciones
fun: función que será llamada desde el modo supervisor.
in: parámetro pensado como de entrada (puede ser NULL o un dato pasado como dirección o la dirección de un dato o datos)
out: parámetro pensado como de salida (puede ser NULL o un dato pasado como dirección o la dirección de un dato o datos)
ret: retorna el valor devuelto por fun(in,out)
NOTA: Obviamente, no hay una regla escrita que diga que los parámetros in y out no puedan ser utilizados ambos como entrada o ambos como salida, por ejemplo. Su designación actual es a título informativo
void swi_mload_led_on(void);
void swi_mload_led_off(void);
void swi_mload_led_blink(void);
Funciones que permiten controlar, el encendido, apagado y la alternancia (parpadeo), del led frontal.
Solo destacar que la función blink no produce un parpadeo de forma automática, si no que alterna entre encendido y apagado cada vez que es llamada.
void swi_mload_os_software_IRQ9_func( int (*system_mode_func)(void));
Función que permite registrar una función que responderá a la llamada de os_software_IRQ(9). Es decir, si se llama a dicha syscall, la función pasada como argumento será llamada en Modo Sistema y su valor de retorno, se tomará como el retorno de os_software_IRQ(9). ¿Posibles usos? Queda a tu discreción
system_mode_fun: función que recibirá el foco al llamar a os_software_IRQ(9)
ret: No retorna nada.
void * swi_mload_log_func(u32 mode, void *buffer_log, int maxsize_log);
Función para trabajar con el buffer que recibe el texto de log desde la función os_puts().
mode: Modo de operación: 0-> retorna la dirección del log buffer, 1-> borra el log buffer 2-> Asigna un nuevo log buffer
buffer_log: dirección del nuevo log buffer (solo con mode==2)
maxsize_log: tamaño del nuevo log buffer (solo con mode==2)
ret: dirección del actual log buffer (por defecto 4KB). El log buffer es una cadena de texto terminada con el carácter '\0'
.align 4
.code 32
.global read_access_perm
read_access_perm:
mrc p15, 0, r0,c3,c0
bx lr
.align 4
.code 32
.global write_access_perm
write_access_perm:
mcr p15, 0, r0,c3,c0
bx lr
u32 read_access_perm(void);
Lee los permisos actuales.
void write_access_perm(u32 flags);
Fija los permisos
.align 4
.code 32
.global ic_invalidate
ic_invalidate:
mov r0, #0
mcr p15, 0, r0, c7, c5, 0
bx lr
void ic_invalidate(void);
Función que invalida toda la caché de instrucciones
u32 syscall_base;
syscall_base=swi_mload_get_syscall_base(); // obtenemos la dirección de la tabla de syscalls
os_sync_after_write((void *) &syscall_base, 4); // aseguramos la concordancia entre caché y memoria
.align 4
.code 32
.global direct_syscall
direct_syscall:
ldr r12, =syscall_base
ldr r12, [r12]
nop
ldr r12, [r12,r11,lsl#2]
nop
bx r12
.align 4
.code 32
.global direct_os_sync_before_read
direct_os_sync_before_read:
mov r11, #0x3f
b direct_syscall
.align 4
.code 32
.global direct_os_sync_after_write
direct_os_sync_after_write:
mov r11, #0x40
b direct_syscall
void direct_os_sync_before_read(void* ptr, int size);
Equivalente a os_sync_before_read()
void direct_os_sync_after_write(void* ptr, int size);
Equivalente a os_sync_after_write()
mload_seek(0x20207c84, SEEK_SET); // dirección en IOS 38 de la cadena DIP a buscar
mload_read(patch_datas, 4); // lee 4 bytes sobre un buffer temporal alineado a 32 bytes
if(patch_datas[0]==0x6e657665)
{
is_ios=38; // es IOS 38
}
else
{
is_ios=36; // suponemos que es IOS 36
}
+0x00 DI_EmulateCmd -> Función de entrada en dip_plugin (Thumb)
+0x04 0x12340001 -> ID de dip_plugin
+0x08 dvd_read_controlling_data -> dirección de buffer de datos en dip_plugin (IOS 36: 0x2022DDAC IOS 38: 0x2022cdac)
+0x0c handle_di_cmd_reentry+1 -> Punto de retorno a DIP (Thumb) (IOS 36: 0x20201010+1 IOS 38: 0x20200d38+1)
+0x10 addr_ios_shared_alloc_aligned+1 -> Función DIP (Thumb) (IOS 36: 0x20200b9c+1 IOS 38: 0x202008c4+1)
+0x14 addr_ios_shared_free+1 -> Función DIP (Thumb) (IOS 36: 0x20200b70+1 IOS 38: 0x20200898+1)
+0x18 addr_ios_memcpy+1 -> Función DIP (Thumb) (IOS 36: 0x20205dc0+1 IOS 38: 0x20205b80+1)
+0x1c addr_ios_fatal_di_error+1 -> Función DIP (Thumb) (IOS 36: 0x20200048+1 IOS 38: 0x20200048+1)
+0x20 addr_ios_doReadHashEncryptedState+1 -> Función DIP (Thumb) (IOS 36: 0x20202b4c+1 IOS 38: 0x20202874+1)
+0x24 addr_ios_printf+1 -> Función DIP (Thumb) (IOS 36: 0x20203934+1 IOS 38: 0x2020365c+1)
+0x28 Reserved
+0x2c Reserved
+0x30 Reserved
+0x34 Reserved
+0x38 Reserved
+0x3c Reserved
+0x40 in_ES_ioctlv -> Punto de entrada de la función ioctlv del módulo dev/es (se usa con 'Skip IOS' en uLoader)
IOS 36 escribió:
memcpy(ios_36, dip_plugin, 4); // copy the entry_point (preserves entry point)
memcpy(dip_plugin, ios_36, 4*10); // copy the adresses from the array
mload_seek(0x1377E000, SEEK_SET); // copy dip_plugin in the starlet
mload_write(dip_plugin,size_dip_plugin);
// enables DIP plugin
mload_seek(0x20209040, SEEK_SET); // enables dip_plugin bypass
mload_write(ios_36, 4);
mload_close();
IOS 38 escribió:memcpy(ios_38, dip_plugin, 4); // copy the entry_point (preserves entry point)
memcpy(dip_plugin, ios_38, 4*10); // copy the adresses from the array
mload_seek(0x1377E000, SEEK_SET); // copy dip_plugin in the starlet
mload_write(dip_plugin,size_dip_plugin);
// enables DIP plugin
mload_seek(0x20208030, SEEK_SET); // enables dip_plugin bypass
mload_write(ios_38, 4);
mload_close();
mload_set_ES_ioctlv_vector(in_ES_ioctlv); // thumb in_ES_ioctlv function
mload_close();
.align 2
.code 16
.global in_ES_ioctlv
.thumb_func
in_ES_ioctlv:
push {r2-r6}
push {lr}
bl ES_ioctlv+1
pop {r1}
pop {r2-r6}
bx r1
.global out_ES_ioctlv
.thumb_func
out_ES_ioctlv:
push {r4-r6,lr}
sub sp, sp, #0x20
ldr r5, [r0,#8]
add r1, r0, #0
ldr r3, = 0x201000D5
bx r3
extern int in_ES_ioctlv(void *);
extern int out_ES_ioctlv(void *);
struct _ioctl{
void *data;
u32 len;
};
struct _ipcreq
{ //ipc struct size: 32
u32 cmd; //0
s32 result; //4
union { //8
s32 fd;
u32 req_cmd;
};
union {
struct {
char *filepath;
u32 mode;
} open;
struct {
void *data;
u32 len;
} read, write;
struct {
s32 where;
s32 whence;
} seek;
struct {
u32 ioctl;
void *buffer_in;
u32 len_in;
void *buffer_io;
u32 len_io;
} ioctl;
struct {
u32 ioctl;
u32 argcin;
u32 argcio;
struct _ioctl *argv;
} ioctlv;
u32 args[5];
};
} ATTRIBUTE_PACKED;
int ES_ioctlv(struct _ipcreq *dat )
{
int r;
u32 ios,version;
ios_sync_before_read(dat, sizeof(struct _ipcreq));
if(dat->ioctlv.ioctl==8) // reboot
{
ios_sync_before_read( (void *) dat->ioctlv.argv[0].data, dat->ioctlv.argv[0].len);
ios=*(((volatile u32 *)dat->ioctlv.argv[0].data)+1) ;
version=1;
ios_sync_before_read((void *) 0x3140,8);
*((volatile u32 *) 0x3140)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision
*((volatile u32 *) 0x3188)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision
ios_sync_after_write((void *) 0x3140,4);
ios_sync_after_write((void *) 0x3188,4);
return 0;
}
r=out_ES_ioctlv(dat);
return r;
}
Hermes escribió:(reservado para escribir tochos)
trigui escribió:Hermes escribió:(reservado para escribir tochos)
Joder, que parece que estás especulando con el terreno "forístico", toma ya, esperando recalificar el espacio, jejeje.
Gracias por tu trabajo Hermes, eres de los grandes.
Un saludo
croatoan escribió:Con todo esto quieres decir que el parcheo sería selectivo y en tiempo real?
Es decir...en un espacio temporal X¿? digamos hipoteticamente un slot entre ios 36 e ios 57 que estuviese inutilizado por ioses legitimos del firmware? ^^
- Juego A - cargo cIOS 202 (Desde la NAND)
- Juego B - cargo cIOS 222 (Desde la sd)
- Juego C - cargo cIOS 223 (Desde el Pdrive)
Corrigeme si escribí alguna tonteria, etc..
Saludos!
rodries escribió:Chinchetorro ya de ya
Mload es lo más importante de la scene de los últimos tiempos
¿Que hay que hacer para que le pongan chincheta?
rodries escribió:Una cosa hermes, deberías poner los fuentes del mload aquí, aunque ya se que se pueden sacar del uloader.
rodries escribió:Otra cosa, a ver si hay alguien que se le de bien el inglés y puede traducirlo, ya que creo que será bastante utilizado por el hombrew
Vrsquid escribió:...
Waninkoko escribió:Hermes, recuerdame que luego te pase la ultima version del DIP plugin, la cual iba a meter en la rev15 (son correcciones y optimizaciones principalmente).
mload es el futuro del cIOS y un gran descubrimiento. Hermes, si necesitas un poco de ayuda, ya sabes
rodries escribió:Chinchetorro ya de ya
Mload es lo más importante de la scene de los últimos tiempos
¿Que hay que hacer para que le pongan chincheta?
Hermes escribió:
Hay que amenazar de muerte a los mods
jamonazo2000 escribió:Nooooo por Dios . Gran trabajo Hermes como siempre,chincheta al canto por peticion popular. Seria muy interesante portarlo todo al wiki cuando este terminado!
jamonazo2000 escribió:Si necesitas cualquier cosa en el post dame un toque
enrikote escribió:Parece ser que mload es un gran avance para la scene, pero hay algunas cosas que no acabo de entender.
¿mload se instala en la wii como si de alguna otra utilidad se tratase o es para programar desde el pc?
¿Con mload ya no hace falta instalar mas cIOS porque la aplicacion ya lo lleva "insertado" dentro y parchea al vuelo?
Y por ultimo:
¿En las proximas versiones de uLoader no será nesesario el cIOS222?
Un saludo!
Hermes escribió:PD: La primera parte de mload, creo que está completa. Si alguien ve algo raro que debiera corregir, que me de un toque . Ya me pondré con la segunda parte en otro momento
rodries escribió:Hermes escribió:PD: La primera parte de mload, creo que está completa. Si alguien ve algo raro que debiera corregir, que me de un toque . Ya me pondré con la segunda parte en otro momento
La primera parte está perfecta.
Ahora viene la que no controlo, ahí si que te voy a achicharrar a preguntas
rodries escribió:Me encantan tus explicaciones.
Una pena haber cambiado de curro y no disponer de tiempo libre para probar. A ver si me toca la lotería para dejarme el curro y puedo ponerme a jugar con tu mload en modo supervidor
Hermes escribió:Actualizado el driver EHCI a la versión 3.1 con una corrección que ignora la condición de error en caso de que se trate de montar LUN 0 sin haber llamado a GetMaxLun (parece que hay algún dispositivo que falla con las nuevas condiciones de error y seguramente se corrija así)
El problema se produce porque hay dispositivos incompatibles con la función GetMaxLun, dado que esa función solo es necesaria si se dispone de varias unidades lógicas (por ejemplo, un lector de tarjetas).
Al mismo tiempo, hay dispositivos que no responden al montaje del LUN si no se llama previamente a la función GetMaxLun. Ante la tesitura y dado que son mayoritarios los dispositivos que no soportan dicha función, se procede a montar el LUN 0 y si falla, entonces se llama a GetMaxLun y se procede a un nuevo intento de montaje del LUN 0, LUN 1... vamos, lo que se soporte.
De esa forma, se trata de cubrir el mayor número de dispositivos. La gestión de errores en la versión 3.0 era diferente y eso hacía que se retornara con error -121 en algunos dispositivos.
Saludos
EDITO:
¿Alguien se atreve a traducirlo al pitinglish?
granberro escribió:Hermes escribió:Actualizado el driver EHCI a la versión 3.1 con una corrección que ignora la condición de error en caso de que se trate de montar LUN 0 sin haber llamado a GetMaxLun (parece que hay algún dispositivo que falla con las nuevas condiciones de error y seguramente se corrija así)
El problema se produce porque hay dispositivos incompatibles con la función GetMaxLun, dado que esa función solo es necesaria si se dispone de varias unidades lógicas (por ejemplo, un lector de tarjetas).
Al mismo tiempo, hay dispositivos que no responden al montaje del LUN si no se llama previamente a la función GetMaxLun. Ante la tesitura y dado que son mayoritarios los dispositivos que no soportan dicha función, se procede a montar el LUN 0 y si falla, entonces se llama a GetMaxLun y se procede a un nuevo intento de montaje del LUN 0, LUN 1... vamos, lo que se soporte.
De esa forma, se trata de cubrir el mayor número de dispositivos. La gestión de errores en la versión 3.0 era diferente y eso hacía que se retornara con error -121 en algunos dispositivos.
Saludos
EDITO:
¿Alguien se atreve a traducirlo al pitinglish?
Aquí tienes/Here you are . No es una traducción literal, me he permitido algunas licencias, pero creo que respeta la idea original.
Saludos
Driver EHCI updated to version 3.1. Fix: Ignore error condition raised when trying to mount LUN 0 without calling GetMaxLun first(there seems to be some devices that fail with new last error conditions added and probably this fix solves the issue).
There are devices that do no support function GetMaxLun. GetMaxLun is only needed when there are several logic units (i.e. a card reader).
On the other hand, some devices do not respond to LUN mount if GetMaxLun is not called first. A dilemma appears, as most devices do no support GetMaxLun. The solution: mount LUN 0, if error arises call GetMaxLun and then try to mount LUN 0, LUN 1, etc.
That way, I try to support as much devices as possible. In version 3.0 errors where handled in a different way so the drive were raising error -121 for some devices.
Hermes escribió: Además, nosotros no sabemos casi nada y tenemos que avanzar a tientas probando cosas.
Hermes escribió:Actualizado el driver EHCI a la versión 3.1 con una corrección que ignora la condición de error en caso de que se trate de montar LUN 0 sin haber llamado a GetMaxLun (parece que hay algún dispositivo que falla con las nuevas condiciones de error y seguramente se corrija así)
El problema se produce porque hay dispositivos incompatibles con la función GetMaxLun, dado que esa función solo es necesaria si se dispone de varias unidades lógicas (por ejemplo, un lector de tarjetas).
Al mismo tiempo, hay dispositivos que no responden al montaje del LUN si no se llama previamente a la función GetMaxLun. Ante la tesitura y dado que son mayoritarios los dispositivos que no soportan dicha función, se procede a montar el LUN 0 y si falla, entonces se llama a GetMaxLun y se procede a un nuevo intento de montaje del LUN 0, LUN 1... vamos, lo que se soporte.
De esa forma, se trata de cubrir el mayor número de dispositivos. La gestión de errores en la versión 3.0 era diferente y eso hacía que se retornara con error -121 en algunos dispositivos.
Saludos
EDITO:
¿Alguien se atreve a traducirlo al pitinglish?
Hello.
I suppose that for now, you should know what a custom IOS is. A custom IOS (Commonly called cIOS) its just an addon or modification of the official IOS to add some functionalities, for example, emulate the DVD drive from a hard disk to load a backup, but it can be used to open new ways for the homebrew, and with that idea i designed mload.
Mload is a module (an application) like any other that composes the IOS, oriented as a tool that gives a way to load other external modules from PPC, to the Starlet and let the applications could provide in a customized way, their own plugins, without the need of install a new cIOS every time you perform a change of any kind. Thus, it facilitates the development and avoiding unwanted collisions and incompatibilities that the CIOS is prepared to work differently.
Working in the Starlet is not easy, because on one hand, we must live with the other modules that make up the IOS, respecting their memory addresses, etc, but there are a number of protections memory and permits that make for example, since I try to write a module in certain areas, there is a crash (for an exception). Furthermore, we know almost nothing and have to grope trying things
So when I developed mload first thing I had to find was a memory area that was free and to provide a space large enough for us to load our modules: the area chosen was between 0x13700000 and 0x1377ffff. That is, 512KB for our purposes, a reasonable space and it seems that not a hassle. I also had to investigate the use of syscalls and learn to run threads and control them, and the use of timers, etc (something for which also will speak later)
General characteristics of mload
mload consists of three sections of memory. The executable code is loaded into the address 0x138c0000, the space of variables, stack etc, from 0x138c8000 and the area that interests us 0x13700000, adjusting the permissions as read / write simply (as you can see in the scripts folder link.ld file in the source of the module).
When you compile the program, we get a module with a size somewhat respectable (about 643 KB!), But this elf module is not ready for that IOS can assimilate it as is and must pass through a tool called stripios (again I refer you the sources) that I modified so that it could compress a section we indicate, at a size of only 1 byte (because it is actually an empty space) using one of the tricks of the elf format.
Thus, the section reduces all its size 0x13700000 (in the makefile you can see the line "@ $ (STRIPIOS) $ <$ @ strip 0x13700000 " that deals with this) and the module to include in the IOS is only 7KB as a result (ie if it is "magic" of good).
Since the PPC (also from the Starlet obviously), we can access a range of functions for manipulating memory modules or run from the device "dev / mload" (later discuss in more detail), mainly in the area of labor between 0x13700000 and 0x1377ffff, of course.
In the last mload version is also supplied a number of new features, whose goal is to win permits to the time to perform certain tasks through software interrupt (SWI) from the Starlet and thus able to make almost anything (for example, I use these tools to change the original interrupt vector and processes from the EHCI module or copy critical Memory areas, with interrupts disabled and all permits).
Mload responds to the service "svc 0xcc”, allowing other service record from 0x00 to 0xff (except 0xcc obviously) and trying the service internally "svc 0xab", specifically (""); function to obtain logs os_puts reported by other modules (obviously, you can register another function to treat 0xab and redirect messages to another device)
Another interesting detail is that mload is loaded using the highest priority allowed (0x79) to enable us to create threads with the widest range of possibilities (a thread can not create another thread that has higher priority than).
Mload identifies the base IOS (between 36, 37,38 and 60) and makes the necessary patches have a direct and internal, but for now, only been able to successfully load based 36 and 38. It also deals with pre-initialize the EHCI driver.
Preparation of cIOS: patches and modules
The current installer, performs two sets of patches: one for the module that I usually call "ES" generically to identify (although it actually contains more things as an elf loader with different sections with the kernel, etc). Another module that is intended to "DIP". In the cIOS 202 the patches designed to facilitate the connection dip_plugin (in binary code that is loaded in the direction that makes 0x1377E000 interface between the module and the module DIP EHCI emulation DVD) are castrated.
The ES patches were transferred to mload module and have the installer replaced by two new patches that act on the table jumps os_software_IRQ syscall (a function which is used to enable a disruption linked to a device), in concrete os_software_IRQ (4) (to enable interrupts EHCI, jumping a check) and os_software_IRQ (9) (break out of use in the Starlet and used here to jump to win crt0.s of mload and system permissions with which to carry out the necessary patches from mload). For practical purposes, a single call to os_software_IRQ (9) during the execution of mload, is what makes it special mload and have become a great power in our hands.
The EHCI module, compile it to be loaded into the address 0x13700000 to 0x1373ffff (occupies half the space!) But we must also say that in fact consists of three modules in one (EHCI USB + Storage + WBFS) and I am providing generous space for the stack and memory allocation.
Both the EHCI module (ehcmodule.elf) as dip_plugin are not loaded by the CIOs, if no external modules that are staying in memory for applications (for example, uLoader) and everyone can distribute to your taste memory as seems (loading other modules or changing their linked addresses)
Tool Box dev / mload
To work with mload from the PPC in examples / ppc / libmload you can find a set of tools with which to perform different tasks.
In mload.h can see the list of available functions. All functions, call mload_init () internally and return a negative value on error. The only special care that we must take is to call mload_close (); before loading a new IOS if we are to continue using the services of dev / mload.
Here lists the main functions:
IOS
int mload_get_IOS_base();
ret: returns the IOS used as the basis of cIOS (FFS, ES, IOSP), currently 38. This is important to know because for example if we change the vector of disruptions, and patches of some kind or even call system functions from the outside, the locations are different for different IOS
Module Load:
int mload_module(void *addr, int len);
Function designed to load small modules from the PPC memory directly. Recommend better use mload_elf()
addr: address of .elf module in memory, aligned to 32
len: length of .elf module
ret: returns negative if any error or id of thread created on Starlet.
int mload_elf(void *my_elf, data_elf *data_elf);
Function used to copy in the Starlet memory different sections of a module in elf format.
my_elf: address of .elf module in memory, aligned to 32
data_elf: structure that is initialized by placing the mload elf starting values, priority, stack, etc.
ret: 0->OK <0 -> Error
int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority);
Function used to run a thread in the starlet (normally is
use in conjunction with the data returned by mload_elf() in data_elf)
starlet_addr: Start address of the program thread
starlet_top_stack: higher stack address (the stack is descendant).
stack_size: total size of the stack.
priority: priority from 0x1 to 0x79 (maximum)
ret: returns negative if was an error or the thread ID created on Starlet.
Read/Write Memory
int mload_seek(int offset, int mode);
Set the pointer on memory for the read/write operations
offset: absolute/relative memory address on Starlet.
mode: SEEK_SET normally.
ret: <0 error
int mload_read(void* buf, u32 size);
int mload_write(const void * buf, u32 size);
read/write from the pointer position on the Starlet
buf: buffer where we read or from we write. Recommended to align to 32
size: bytes to read or write
ret: <0 error. Number of bytes readed/writed.
int mload_memset(void *starlet_addr, int set, int len);
special function equal to memset() to the Starlet.
Especials
void * mload_get_ehci_data();
Returns EHCI structure’s address. Used in ehcmodule.elf.
int mload_get_log();
Function that sets the read/write pointer in the start address of the area reserved for the logs through the os_puts() function. Normally it have a size of 4KB and its composed by a chain of characters finishing on '\0'.
It would be followed by a mload_read() and its utility is to pull over its content to the SD, for example.
ret: if fails return <0. Returns the maximum size of the log buffer.
int mload_getw(const void * addr, u32 *dat);
int mload_geth(const void * addr, u16 *dat);
int mload_getb(const void * addr, u8 *dat);
int mload_setw(const void * addr, u32 dat);
int mload_seth(const void * addr, u16 dat);
int mload_setb(const void * addr, u8 dat);
Special functions t oread/write records or non-cached memory areas accesable in user mode from Starlet.
Syscalls
Well, you know mload functions that allow us to read / write / load and run in memory, or code modules from the PPC, but of little use unless we know that a number of functions that provides the system to build these modules and able to communicate with them. So I think we could really give a review of the main syscalls that we may need to work with our modules.
First of all, I would point out that we can not use certain standard library functions like malloc (), free () or printf ().
Memory
int os_heap_create(void* ptr, int size);
Function used to create a mound of memory what we use privately on our module Put another way, it's as if I shall indicate in a program memory area which is then allocated with malloc () (which I mentioned that should not be used)
ptr: start of the area used as a mound (aligned to 32, probably)
size: size in bytes of the mound
ret= <0 error. Id of Heap
void* os_heap_alloc(int heap, unsigned int size);
Equal to malloc().
heap: id of heap to use. 0 seems the global mound
size: size in bytes to asign.
ret= addres of the asigned memory or NULL
void os_heap_free(int heap, void* ptr);
Equal to free().
heap -> id of heap.
ptr ->address to free
Caché
As you know, the processors to implement cache memory to speed the memory access, because the RAM is slower, for both, execution code (icache) as for data (dcache). The following syscalls are used to work with the data cache.
void os_sync_before_read(void* ptr, int size);
Function used before the readings of the processor when it is assumed that DMA (out of cache) operations have been made on the specified memory block. I have a doubt about this function that i dont checked; i dont know if simply invalidates the cache, or if in addition procedes to refresh it (in the first case, the cache lines will be proggressive refreshed when we read from the RAM, so if we invalidates the cache and a second later, for example, we change a data via DMA and later we read that data, we will get the actual value, because the cache will be refreshed in the moment we read it, while in the second case, we will get the old data, because the cache was refreshed when we call os_sync_before_read() and the reading is done inside the cache (unless for some external reason, the processor would have dismissed the data) and the changes cannot be seen out of the cache, unless we call again os_sync_before_read().
I just known that the ARM has a specific function to invalidate cache, but there are some operations somekind strange that suggest that can exist an invalidation and refresh of the cache.
ptr: memory address. Be careful with this, because the cache lines work with 32 bytes, so if the data overlaps (ie, the pointer is not aligned to 32) we will get unexpected results
size: size in bytes. Watch out because the size is rounded to be divisible by 32 and if the data is overlapped (ie, that exceed the size rounding) we will get unexpected results
void os_sync_after_write(void* ptr, int size);
Function used after scripture when it is presumed to use the memory block specified in DMA operations (not cache), so that the cache is written into RAM.
ptr: memory address. Be careful with this, because the cache lines work with 32 bytes, so if the data overlaps (ie, the pointer is not aligned to 32) we will get unexpected results.
size: size in bytes. Watch out because the size is rounded to be divisible by 32 and if the data is overlapped (ie, that exceed the size rounding) we will get unexpected results
Queues
Queues is the mechanism that allows us to synchronize the execution of different threads, so that we can register a queue for a device for example and it awaits that sends a message that may be the direction of a data structure or simply a sign of another kind.
int os_message_queue_create(void* ptr, unsigned int id);
Function to create a queue.
ptr: Pointer where we receive the queue’s messages.
id: Number of entries that bear the queue. Each entry occupies 4 bytes in memory identified by ptr. You must be very careful not to send more messages from the queue to bear (especially dangerous when working with timers). Indeed, the label 'id' should be a 'MAX_ENTRIES' or something similar.
ret: <0 if error. Queuehandle associated with the queue.
int os_message_queue_receive(int queue, unsigned int* message, unsigned int flags);
Function that waits until a queue receives a message, interrupting the execution of the thread.
queue: queuehandle (returned by os_message_queue_create())
message: if its a device, would normally be the address of a structure ipcmessage (see syscalls.h)
flags: ever 0
ret: <0 error. Its possible return the number of elements in queue, but I have not checked.
int os_message_queue_send(int queue, unsigned int message, int flags);
int os_message_queue_send_now(int queue, unsigned int message, int flags);
Function to send a message to a queue. Probably the send_now provoques the activation of the thread omiting the priority of the queues.
queue: queuehandle (returned by os_message_queue_create())
message: address or message to send to the queue.
flags: to 0
ret: <0 error
void os_message_queue_ack(void* message, int result);
Function that returns the answer of the device while IPC. Really, i cant see the relation between this and the queses, because this is a function is to retrieve the answer of a device.
message: address of the structure ipcmessage received from os_message_queue_receive()
result: Result of the requested IPC operation (IOS_OPEN, IOS_CLOSE, ...)
Devices
int os_device_register(const char* devicename, int queuehandle);
Function that associates a device name to a queue for IPC operations.
devicename: Name of the device: Ie "/dev/mload"
queuehandle: queuehandle (returned by os_message_queue_create())
int os_open(char* device, int mode);
Equal to IOS_Open in PPC.
device: Name of the device to open.
mode: mode flags (O_CREAT, O_TRUNC, O_RDONLY, O_WRONLY, O_RDWR ... in include/sys/fcntl.h)
ret: <0 error, fd of device.
int os_close(int fd);
closes the device.
fd: fd of device
ret: <0 error
int os_seek(int fd, int offset, int mode);
Sets the position of the device pointer.
fd: fd of device
offset: position on the device
mode: SEEK_SET, SEEK_END, SEEK_CUR...
ret: <0 error.Current absolute position in the device
int os_read(int fd, void *d, int len);
int os_write(int fd, void *s, int len);
Read/write functions on the device
fd: fd of the device
s: d: source/destiny address for reading/writing
len: length in bytes.
ret: <0 Error. Number of bytes readed/writed.
int os_ioctlv(int fd, int request, int bytes_in, int bytes_out, ioctlv *vector);
Function to control I/O by vectors. Should use the appropiated functions for cache data transfers.
fd: fd of device
request: function ioctlv index
bytes_in: Number of input elements (the label isn’t good, because aren’t bytes)
bytes_out: Number of output elements(the label isn’t good, because aren’t bytes)
vector: Address of the vector array (ioctlv type in syscalls.h). in+out
ret: <0 error. Result depends on the selected function.
int os_ioctl(int fd, int request, void *in, int bytes_in, void *out, int bytes_out);
I/O control function. Should use the functions appropriate in cache data transfers.
fd: fd of device
request: ioctl function index
in: Input buffer address
bytes_in: input buffer length
out: output buffer address
bytes_out: output buffer length
ret: <0 error. Result depends on the selected function.
Timers
The timers are used to send a message to the queue every so often. Internally, they are controlled by an alarm that generates an interrupt every so often, so the time if the timer is very low, probably has some distortion (apart from the activation of the queue depend on the priority of threads, etc. ). The resolution of the timers is measured in microseconds. Be especially careful that the repetition time is not too short and will finally exceed the associated queue.
int os_create_timer(int time_us, int repeat_time_us, int message_queue, int message);
Function to create a timer.
time_us: Time in microseconds of the first count
repeat_time: Time in microseconds of the following counts. If a single account needed, we can fix a high time here and proceed to stop the timer when the message is received.
message_queue: queuehandle of the handle that will receive the message
message: Message that the queue will receive when the timer expires (it can be the address of a structure, for example)
ret: <0 error when creating the timer. id of the timer handle associated
int os_stop_timer(int timer_id);
Stops the counting of a timer. It can be restablished with os_restart_timer().
timer_id: Timer handle
ret: <0 error
int os_destroy_timer(int time_id);
Eliminates a timer. It’s possible that may be needed to stop it first with os_stop_timer().
timer_id: Timer handle
ret: <0 error
int os_restart_timer(int timer_id, int time_us);
Restarts a timer stopped with os_stop_timer(). The counting is repetitive, La cuenta es repetitiva, so beware that the time is not too short to produce an overflow in the queue associated with the timer.
timer_id: Timer handle
time_us: Time in microseconds of the following repeatings.
ret: <0 error
int os_timer_now(int time_id);
Probably forces a timer to send the message and restart the counting when called
timer_id: Timer handle
ret: <0 error
Interruptions
int os_register_event_handler(int device, int queue, int message);
Register a queue to a device what will receive events by interruptions. Only possible to register if there’s no other event registered.
device: IRQ of the current device 4->EHCI, 5->OHC0, etc (see libcios/include/starlet.h)
queue: quehandle of associated queue
message: message that will be sent to the queue
ret: <0 si error
int os_unregister_event_handler(int device);
Eliminates the events interruptions for the specified device.
device: IRQ of the current device 4->EHCI, 5->OHC0, etc (see libcios/include/starlet.h)
ret: <0 if error
int os_software_IRQ(int dev);
A feature that enables the use of interrupts in the device. It has a dual function: firstly, it eliminates a possible interrupt pending for that device and the other, adjust the interrupt mask (Hollywood) so that these may occur. However, it only supports syscall fix from IRQ4 (EHCI) onwards and there is a check (probably linked to the process ID) does not in all cases interruption can fix it from here (hence one of the patches of mload to be devoted to enable os_software_IRQ(4).
However, it’s possible to access to the records HW_ARMIRQFLAG and HW_ARMIRQMASK directly from the SWI services of mload (as we see lately)
dev: IRQ of the current device from 4->EHCI, 5->OHC0, etc (see libcios/include/starlet.h)
ret: <0 error
Threads
NOTE: The thread ID=0 indicates the actual thread in some functions.
int os_thread_create( unsigned int (*entry)(void* arg), void* arg, void* stack, unsigned int stacksize, unsigned int priority, int autostart);
Function that creates a thread of the program in the starlet. The thread is standing start and requires the use of os_thread_continue () to run.
entry: Start function that will receive the focus of the thread.
arg: argument that is passed to the function start
stack: as the maximum memory address stack. For example, if we use u32 my_stack [STACK_SIZE] as a stack of the thread, we'd have to go & my_stack [STACK_SIZE] as the address
stacksize: Tamaño en bytes de la pila
priority: Priority between 0x1 and 0x79. The priority can not be greater than the thread where this function is invoked (or not work)
autostart: to 0. There seems to function as autostart and you may not have or would use to indicate otherwise.
ret: <0 Error. ID of thread.
int os_thread_continue(int id);
Continuing with the execution of the thread (after the establishment or a stop)
id: ID of thread
ret: <0 error
int os_thread_stop(int id);
Stops the execution of a thread.
id: ID of the thread
ret: <0 error
int os_get_thread_id(void);
Returns the ID of the current thread.
ret: ID of the thread.
void os_thread_set_priority(int id, unsigned int priority);
Sets the priority of the current thread.
id: ID of the thread
priority: Priority beetween 0x1 and 0x79. The function will fail if the priority is higher than the creator of the thread.
int os_thread_get_priority(void);
Returns the priority of the current thread.
ret: <0 error. Current priority.
Log
void os_puts(char *str);
Function that sends a text chain to the log buffer
Source Codes
Before looking at some example code, I would like to explain a little organization of mload sources, that ye may know where to find information:
apps
|--- cios_installer -> Installer of cIOS 202/222/223
cios_mload
|
|------- cios_installer -> cIOS installer sources
|
|------- ehcmodule -> Sources of EHCI module
|
|------- examples -> Examples for PPC and Starlet. Mload library for PPC
| |
| |---- ppc
| | |------ example1 -> Simple example that shows the activity of a module
| | |------ example2 -> Example that shows the use of a FAT/FAT32 module
| | |------ libmload -> Library used in the " mload ToolBox " | |
| |---- starlet
| | |------ example1 -> Module that shows the use of timers, queues and multithread (to reset a count)
| | |------ libfat -> module that supports FAT/FAT32 with SD memorys
| |
| |---- stripios -> stripios utility replica
|
|------- libcios -> Base librarys
| |
| |------ include
| | |------- starlet.h -> Information about records (Hollywood) IRQ, GPIO, TIMER (from wiibrew.org)
| | |------- swi_mload.h -> mload’s SWI services library (Advanced)
| | |------- syscalls.h -> Syscalls library
| | |------- types.h -> Types of data used in the Starlet
| |
| |------ source
| |------- swi_mload.c -> mload’s SWI services library (Advanced)
| |------- swi_mload.s -> Assembler code to call SWI
| |------- syscalls.s -> Assembler code to call the syscalls
|
|------- mload -> mload module sources
|
|------- stripios -> Utility which converts the normal elfs needed by the IOS
|
|------- tinyehci -> Sources of the EHCI /USB Storage driver
|
|------- wii_wbfs -> Sources to work with WBFS partitions
Module organization
The modules in the Starlet, are compiled in a fixed direction and it is necessary to convert the resulting elf by stripios utility. I will explain here how they are structured based on the module example1 sources:
example1
|
|------- bin -> Here will stay the resulting module.
|------- scripts -> Here is the nostart.specs and link.ld. The latter contains the module address map |------- source
| |---- crt0.S -> This sets the initial priority of the module, stack, etc.. And other assembly routines.
| |---- main.c -> The module body.
|
|------- Makefile
Creating a thread and a timer to activate it
First, we must declare an area to use for the stack:
#define THREAD_STACK 1024
u8 thread_stack[THREAD_STACK] __attribute__ ((aligned (32)));
Then we declare a function that will receive the focus of the thread:
int thread_start(void *arg)
{
/* create a queue that will use the thread to wait for an event that actuates
note that in this case we are using as heaphandle 0, to allocate 32 bytes and a queue of 8 items (4 bytes)
*/
int thread_queuehandle = os_message_queue_create( os_heap_alloc(0, 0x20), 8);
/* we define an event (in this case a timer) that sent a message that activates the thread every 10 seconds */
os_create_timer(1000*1000*10, 1000*1000*10, thread_queuehandle, 0x555);
while(1)
{
u32 message;
/* waits for the queue to receive a message */
os_message_queue_receive(thread_queuehandle, (void*)&message, 0);
if(message==0x555)
{
// message from the timer received: do here as appropiate
}
}
return 0;
}
Now we must create the thread from main():
int my_thread_id=os_thread_create( (void *) thread_start, NULL, &thread_stack[THREAD_STACK], THREAD_STACK, 0x48, 0);
if(my_thread_id>=0) os_thread_continue(my_thread_id);
And with this example, I will complete the basic information. You have enough mload code in both, as in the examples ehcmodule to see other uses and does not make sense for me to dwell more on this topic. In the next post I have reserved, almost then try to explain the functions of SWI mload and some more advanced uses.
mload sources download
http://mods.elotrolado.net/~hermes/wii/ ... l_3.1B.rar
if(use_port1)
// release port 0 and use port 1
{
u32 dat=0;
u32 addr;
// get EHCI base registers
mload_getw((void *) 0x0D040000, &addr);
addr&=0xff;
addr+=0x0D040000;
mload_getw((void *) (addr+0x44), &dat);
if((dat & 0x2001)==1) mload_setw((void *) (addr+0x44), 0x2000);
mload_getw((void *) (addr+0x48), &dat);
if((dat & 0x2000)==0x2000) mload_setw((void *) (addr+0x48), 0x1001);
}
Part II: Advanced Applications
So far we have seen enough to understand how we can build our own modules, where and how to load using the tools does not provide mload. But we want to go even further: as we were able to access the Starlet with the ability to upload your own code, why not have some tools that allow us to even change the system and skip the limitations imposed on us?
To understand it more fully below, we use the information that can be found in relation to wiibrew.org Starlet, this particular pair of links:
PDF 1: http://infocenter.arm.com/help/topic/co ... 26_TRM.pdf
PDF 2: http://rtds.cs.tamu.edu/web_462/techdoc ... S_r1p2.pdf
For brevity, I refer to them as PDF 1 and PDF 2, when you want to point out some important information.
There is also a link on architecture in general: http://www.arm.com/miscPDFs/14128.pdf
Execution modes and exception vectors
Our modules are executed in user mode, in which we do not have permissions to modify other parts of memory or modify certain records and therefore we can only observe the content and sometimes not even that and the only way to do something, syscalls is available through ... But really, is the only way?
Obviously, it is necessary to gain these permits, in fact, the syscalls that I have described above, are just a way to access an exception that occurs when you try to run an ARM instruction set (ie, a machine instruction nonexistent), causing a mode shift from processor and a jump to a certain point of memory, where if you have some of the permits we need to do some of the interesting things that we want
In Wii exceptions table is located from address 0xFFFF0000 and according to the PDF 2, page 70, these are the vectors containing:
OFFSET 0xFFFF0000 Reset (Supervisor Mode)
OFFSET 0xFFFF0004 Undefined Instruction (indefined mode). The Wii syscalls we know
OFFSET 0xFFFF0008 SWI Software Interrupt (Supervisor Mode): Jumps to a return statement in the IOS, except in mload
OFFSET 0xFFFF000C Abort (Prefech) (Abort Mode)
OFFSET 0xFFFF0010 Abort (Data) (Abort Mode)
OFFSET 0xFFFF0014 Reserved
OFFSET 0xFFFF0018 IRQ: Iterrupts (IRQ Mode)
OFFSET 0xFFFF001C FIQ: Fast interrupts (FIQ Mode). Without use in Wii
As we can see, each exception comes into a certain mode of the processor whose main difference is, apart from having a series of permissions that are not user-mode, having a number of additional registers to store the stack or return different, apart from having other state records. In addition to the ways that you can see there, so we have the system mode, which relies on the records of User mode, but obviously the difference is that we permit those who do not have in the User mode.
Indeed, one important thing to know is that every time there is an exception interrupts are disabled in the CPSR register.
If you want to take a look at the table of records for different modes available in PDF 2, page 55 we can find (ARM or 32-bit mode) and in 57 the way for the Thumb mode.
For if you are not familiar with the ARM (I’m not to be an expert, mind you: but I have some good ideas), ARM have a full instruction mode, and a 32-bit reduced instruction mode, called Thumb only 16 bits, saving memory and has also limited access to certain records. The code compiled in C becomes Thumb instructions, while the 32-bit ARM mode is reserved for special operations (such as calls to syscalls) or routines that we want to optimize for speed. In the PDF you can find information on the various modes of operation, if you are interested.
Indeed, in the same PDF 2, page 58, you can see the status register CPSR with the description of the different flags (Run Mode, Thumb, Flags than IRQ and FIQ disabled by software and the typical state flags that have any processor).
The Fight Club
" The first rule of Fight Club is that no one speaks of Fight Club " ( )
Well, here is where I break the first rule to tell you my adventures and misadventures in the EHCI driver development and working with the system.
It seems obvious that if we want to obtain permits, or should try to seek some kind of strange that we exploit plants in System Mode (because either I am stupid, or User mode CPSR registration happens when you try to change mode, thing which is logical because if not, for why permissions serve?) or much easier, since we have the ability to create our cIOS, in part to modify any of these exceptions we have seen, we get back the control.
Months ago, I suggested Waninkoko that could be used the syscalls, either by using any "instruction" not to use or modify any manner which could do its job and it gives us some control, but the truth is I was not interested in carrying it out personally and had other concerns.
But unfortunately, the EHCI driver gave me many problems that seemed rare crashes due to problems of synchronization with other threads or interruptions in uncomfortable spots (it seems that is a bug in IOS 36, but how the hell would I assume that?) and as I had contained about 100 times for punching the Wii against the wall, the result of helplessness and despair, I thought of looking at how to use interrupts.
Disassembly from the vector of syscalls, easily find the table breaks the processing functions and in particular those syscalls for os_software_IRQ() function, which is failing to setting IRQ 4.
Curiously, this function also uses a jump table to manage the different breaks and was easy to realize that enables secure them from IRQ 4 and had a series of interruptions using a common routine return...
Looking at IRQ 4, I was doing a check that if not met, would not allow setting the appropriate flags for that IRQ. So I calculated the necessary leap to avoid the check at the same time as adopting the IRQ 9, which was not in use, to jump to a point crt0.s of mload, in order to obtain permits (are the two current patch made in the CIOS installer)
The result is that although it was enabled IRQ 4, did not work (because I needed information that led to create this thread hilo_divagaciones-sobre-ehci-y-su-vector-de-interrupcion_1277800), IRQ 9 but if it worked perfectly, as shown on the front LED (one of the records is prohibited in User Mode).
Thanks to Isobel knew what I needed to start interruptions (a flag in one of the EHCI registers reserved), but no problems were resolved and also had new ones, because of the possibility of the occurrence of multiple breaks blow (especially when I went to use my own interrupts vector).
However, dissassemble programs produces that indirectly see things you’re not looking for.
Sin embargo, desensamblar programas produce que indirectamente, observes cosas que no vas buscando. Between that and the PDF, I had realized that the SWI vector was out of use, which arrived here with interrupts disabled, in different way than with the syscalls and could use a series of own special instructions to call different routines.
Especially needed access to the cache-related syscalls and that this did not involve extreme complication.
The truth is that I remember a conversation with marcan of long ago, about the mess they were assuming the functions of cache (invalidate / flush) because apart from the mandatory in the MRA, had to fool around with an unknown number of records from time he developed BootMii. In fact, if you have you lost an eye to MINI can see that calling a function rarer than a green dog, to close these transactions.
So one of my ideas was to see if I could somehow call the syscalls without having to resort to using syscalls table to call them directly (as in every IOS is usually placed in a different location) or having to copy part of MINI ... at least for such cache operations (necessary if you modify things hot). And that was even more interesting to work with instead of SWI os_software_IRQ (9), who also brushed past one of the parameters that function as records. In addition there was the appeal of being able to capture system messages, because I knew the use of svc 0xab in os_puts() function of libcios and had seen references to it in the modules.
Total that was the origin of the SWI vector processing, where I deposited some of my confidence, believing that if you disable interrupts at critical points, perhaps resolving the errors so strange EHCI driver (as you had a mistake the minute that you had after an hour ... and then five errors in a very short interval and then another hour and a blockage which required unplugging. So strange).
I soon realized it would not be possible to use syscalls as syscalls (passed by indefinitely in System Mode and this means that for example, the stack does not match, or the return), and even being in "God Mode" , who could not access certain parts of the report (for example, could not patch dev / s), but fortunately, the vector itself shown as IRQ and I fixed the permissions in Access Client mode for all domains and the PDF contained information about it (PDF 1, page 47. In crt0.s of mload, you can see two functions read_access_perm() and write_access_perm() related to this)
At the end was fully functional, but still had a driver unstable ... then it occurred to me to test with IOS 38, as it already had its DIP module adapted to work with uLoader and fuck it was very rare to be a driver problem and since I had run out of possibilities (that was, or hitting the Wii against the wall, followed by the hard drive) and .... EUREKA! not a damn reading error and all stable: it was only cleaning up the code of things that were not necessary and try to add the new service SWI anything that might be useful and necessary not only to work with the EHCI driver or uLoader if not with a view to a more profound and general.
"The golden rule of Fight Club is that if you dont surrender, you may not get what you expected at first, but you can also get other rewards in return. Sometimes even you have some luck and you get much more than initially expected "
Thanks to that, now I have a very stable driver, without a read error on my drives, work interruptions and a set of tools that allow me full access and the ability to change the IOS (hot though: after a good amount of work and jeopardizing my mental stability, health of the Wii, the hard disk and the wall)
SWI Services of mload
The SWI vector is captured and prepared to respond to the assembler instruction "svc x" (you can also write as "swi x", if I remember correctly). When you call instruction, an exception occurs and is the responsibility of the role of treatment of such exception, for the instruction code and get the arguments "x". The svc instruction is also available in thumb mode, but not if the exception or limitation for any problems, I managed to work only if it is aligned to 4 bytes.However, it is preferable to use 32-bit version, in my view.
Moreover, to know that interruptions should remain disabled by software and which provides a stack of 0x900 bytes to work with the different roles SWI, of which few will be spent on keeping records, etc., so it must be take special care to ensure that local variables, do not occupy much space.
Ah! and obviously ... should not call a function that uses SWI from a SWI function.
Mload services are offered in "svc 0xcc" and libcios as I said, there is swi_mload library to facilitate access to these services. Obviously, you can only access these functions from the Starlet.
swi_mload
These are the offered functions:
void swi_mload_add_handler(u8 svc_code, int (*func) (u32 arg0, u32 arg1, u32 arg2, u32 arg3));
Function thats lets you create a handler to deal with "svc x" where x can be any value between 0x0 and 0xff, except 0xcc always be controlled by mload. The reason for this function is to allow others to add their own modules to exchange data vector, for example. The "svc 0xab" is registered internally to redirect os_puts (), but can be changed through this function, for treatment from another location.
svc_code: from 0x0 to 0xff except 0xcc. For example 0xab for "svc 0xab"
func: function that deals with the new SWI service. The function receives the arguments as records from the r3 and r0 to be executed in Supervisor mode, so that access other extra arguments, it would be rather complicated (the C functions are the first 4 arguments in registers r0 to r3, and the rest in the stack). The return value is returned as in any other function of C (log r0)
ret: do not returns anything
void * swi_mload_EHCI_data(void);
This function returns the EHCI pre-initialized structure from mload (the same as that obtained using the equivalent function in dev/mload)
Its use is limited to ehcmodule.elf
u32 swi_mload_get_syscall_base(void);
Gets the address table syscall functions. From this direction, we can call some syscalls directly.
For example, if we call os_sync_before_read (), we must call the syscall 0x3f. Based on the direction that returns this function, we can calculate that: syscall_base + 0x3f * 4 is the address of the function we need.
Later, i will describe this practical shortcut, but you can watch both at the main crt0.s and ehcmodule, as in mload sources.
ret: Address of the function table of syscalls
u32 swi_mload_get_ios_base(void);
Returns the IOS version used as basis (now 38). Can detect IOS 36,37,38 and 60. This is useful to adapt the patch we requireby a dynamic way (eg ehcmodule use this function to adjust the routine will interrupt vector for each IOS)
ret: base IOS.
void swi_mload_memcpy(void * dst, void * src, int len);
Function that copy a block of memory from the source address to destination address
What makes it special is:
-1) The copy is done from the Supervisor Mode and with interrupts disabled
-2) It is stored and access permissions are set as Manager, so there will be no access failures
-3) Function memcpy() to copy the memory block
-4) Direct access to the role of os_sync_after_write syscall () to copy escritosse data cache to RAM
-5) Restores the original access permissions
In practice is to copy a block of memory into the cache to another point without major interruption, or failure of access (for lack of permits) and synchronizing the cache data in writing to the RAM (to be used with a DMA, for example)
void swi_mload_memcpy_from_uncached(void * dst, void * src, int len);
Function that copy a block of memory from the source address to the destination address, invalidating the first cache data from the source address
What makes it special is:
-1) The copy is done from the Supervisor Mode and with interrupts disabled
-2) Is stored and access permissions are set as Manager, so there will be no access failures
-3) Direct access to the role of os_sync_before_read syscall() to read data from memory to update the cache RAM
-4) Function memcpy() to copy the memory block
-5) Direct access to the role of os_sync_after_write syscall() so that the written data is copied from the cache to RAM
-6) Restores the original access permissions
In practice is to copy a memory block from outside the cache (data from a DMA, for example) to another point without major interruption, or failure of access (for lack of permits) and synchronizing data write cache with RAM.
u32 swi_mload_get_register(u32 addr);
Reading a 32-bit register
addr: address of record
ret: register value
void swi_mload_put_register(u32 addr, u32 val);
Writing a 32-bit register
addr: address of record
val: value to write
ret: no return anything
void swi_mload_set_register(u32 addr, u32 val);
Sets bits in a register of 32 bits. Equivalent to *(addr)|=val; The bits are set to 1
addr: address of record
val: value to set bits
ret: no return anything
void swi_mload_clr_register(u32 addr, u32 val);
Clear bits in a register of 32 bits. Equivalent to *(addr)&=~val. The 1 bits are cleared
addr: address of record
val: value to the bits to remove
ret: no return anything
int swi_mload_call_func(int (*func) (void *in, void *out), void *in, void *out);
Function that is used to invoke another function provided by the user, to be called in Supervisor mode, with interrupts turned off. It is important that interrupts remain disabled in that function (do not change the flags of the CPSR register. With this function, it is unnecessary to extend the services of mload SWI in the future. It has a stack with 0x900 bytes, so watch the size of local variables. Also it is recommended that the functions are relatively fast, not to interfere with the response to disruptions
fun: function to be called from supervisor mode.
in: intended as input parameter (may be NULL or data passed as address or the address of a point or points)
out: output parameter as thought (may be NULL or data passed as address or the address of a point or points)
ret: return the return value of fun(in, out)
NOTE: Obviously, there is no written rule stating that the parameters in and out can not be used both as input and output, or both, for example. His current appointment is for information
void swi_mload_led_on(void);
void swi_mload_led_off(void);
void swi_mload_led_blink(void);
Features that control the on, off and alternation (blinking), the front LED.
Just note that the function does not produce a blink automatically, if not alternating on and off each time it is called.
void swi_mload_os_software_IRQ9_func( int (*system_mode_func)(void));
A feature that allows you to register a function that will respond to the call os_software_IRQ(9).That is, if the syscall is called, the function passed as argument is called Mode System and its return value, is taken as the return of os_software_IRQ(9). "Possible uses? At your discretion
system_mode_fun feature that will receive focus when calling os_software_IRQ(9)
ret: no return anything.
void * swi_mload_log_func(u32 mode, void *buffer_log, int maxsize_log);
Function to work with the text buffer that receives the log from os_puts function().
mode: Mode of operation: 0-> returns the address of the log buffer, 1-> clears the buffer log 2-> Set a new log buffer
buffer_log: new log buffer address (only with mode == 2)
maxsize_log: new log buffer size (only with mode == 2)
ret: current log buffer address (default 4KB). The log buffer is a string terminated with the character '\ 0'
Trabajando en Modo Supervisor
Swi_mload_call_func function() and swi_mload_add_handler function() show us ways to enter Supervisor mode. Once inside, we can not use calls to syscall and may need to access special features that while I can not provide all the data, if I can provide a range of functions that may be necessary and useful.
Reading and writing the access permissions.
In crt0.s of mload, we can see:
CÓDIGO: SELECCIONAR TODO.align 4
.code 32
.global read_access_perm
read_access_perm:
mrc p15, 0, r0,c3,c0
bx lr
.align 4
.code 32
.global write_access_perm
write_access_perm:
mcr p15, 0, r0,c3,c0
bx lr
The functions are defined as:
u32 read_access_perm(void);
Read current permissions
void write_access_perm(u32 flags);
Fix the permissions
And you can get full access write_access_perm (0xffffffff);. Obviously, it should reset the permissions after the end of whatever they're doing (this normally only used when we play a memory area prohibited). The description of the permits can be viewed in PDF 1, page 47
Invalidate the instruction cache
When we want to do some kind of patch for the code running, we may want to make sure that the instruction cache is updated with the proper values. Normally, we control the data cache, but do not have any syscall to do the same with the instruction cache so we must address ourselves. In the PDF 1, page 51 we can see the processor instruction required
In crt0.s of mload we can see:
CÓDIGO: SELECCIONAR TODO
.align 4
.code 32
.global ic_invalidate
ic_invalidate:
mov r0, #0
mcr p15, 0, r0, c7, c5, 0
bx lr
And is defined as:
void ic_invalidate(void);
Function invalidates the entire instruction cache
The problem is that theoretically, there are Wii to do more to update the cache, so it would be best is to invoke this function and then call the data cache instructions with which we have on the area changed.
Syscalls access
But with what functions we have to work with the data cache if we can not call the syscalls directly?
That's where swi_mload_get_syscall_base() function.
If you look on the crt0.s of main.c and ehcmodule, you can see that in this variable is declared main.c:
u32 syscall_base;
and from the main() calls the function of this form (one time):
syscall_base=swi_mload_get_syscall_base(); // get the address of the syscalls table
os_sync_after_write((void *) &syscall_base, 4); // ensure consistency between cache and memory
With this we have adjusted all measures necessary to invoke syscalls directly, using its table hops. In crt0.s we observe:
CÓDIGO: SELECCIONAR TODO
.align 4
.code 32
.global direct_syscall
direct_syscall:
ldr r12, =syscall_base
ldr r12, [r12]
nop
ldr r12, [r12,r11,lsl#2]
nop
bx r12
.align 4
.code 32
.global direct_os_sync_before_read
direct_os_sync_before_read:
mov r11, #0x3f
b direct_syscall
.align 4
.code 32
.global direct_os_sync_after_write
direct_os_sync_after_write:
mov r11, #0x40
b direct_syscall
As can be seen, the register r11 is used to contain the index to the syscall, while the r12 register is used to store the address of the table and calculate the offset needed to invoke the requested syscall function.
You must keep in mind that the threads related syscalls surely fail, to not be working in the right context, but really, you need not use them here.
The syscalls that I have shown, are declared thus:
void direct_os_sync_before_read(void* ptr, int size);
Equal to os_sync_before_read()
void direct_os_sync_after_write(void* ptr, int size);
Equal to os_sync_after_write()
The interrupts vector:
In crt0.s of ehcmodule, you can see the routine interrupt_vector(). The routine is set to work with IOS 36, but as seen in main.c in copy_int_vect function() are conducted crt0.s some patches to adjust the routine to work with IOS 36,37,38 and 60.
The capture mode is done in vector instruction "tst r8, # 0x1" of original vector, modifying the instruction and the next for a "ldr pc,=interrupt_vector. You can see in main.c "static u32 array [2]=(0xE51FF004, 0);" used to carry out the necessary patches (the second value would be the jump address)
Registry r8, stores the interrupt flags, having been masked by the mask of interrupts. Therefore, a bit rate means an interruption in progress.
The interrupt vector makes a series of checks and chain break between is EHCI, responding to the statement "tst r8, # 0x10"
We therefore such verification and if there has been no interruption, we check "tst r8, # 0x1" original (vector of timer) and jumped into the convenient point in the original vector by virtue of the result it gives.
If the interruption EHCI is in the process, the statement "bic r8, r8, # 0x10" ensures that no such interruption try again later when we get back to the original interrupt vector.
The instructions "mov r2, # 0x10" and "str r2, [r7] are committed to cancel the interrupt flag in the interrupt register (registry 0x0d800038, you can look at those records in libcios, specifically starlet.h)
Then we saw a series of instructions to preserve the stack, switch to another internal (0x200 bytes) and how to preserve a series of records, before jumping to _ehci_vector_, from which you will jump ehci_vector(), thumb mode ( ehci_vector() is declared in ehci_interrupt.c)
A round is a test with the return: returns 1 if it is to call the function int_send_device_message (4) after having restored the battery, (this option is not used now because that function is invoked from within the ehci_vector()) try going after "tst r8, # 0x1" (the interruption of the timer) and returning in the original vector consequences
Int_send_device_message function () is mandated to send a message to the queue associated with the device, if it was registered with the os_register_event_handler() syscall. In ehcmodule, when we are transferring data, the thread set a timer (timeout) and waits for the associated queue receives a message, either from the cessation or timer.
I hope that with these notes, you know how it works to understand the interrupt vector.Obviously, it is necessary to know some ARM assembler, have a good disassembler (like IDA Pro) and do the same starting from the table of exceptions that you put up, and in particular from the IRQ vector to get a better view appropriate topic.
DIP Plugin (cIOS 222/223)
dip_plugin is a binary module (does not use format .elf) developed by Wiigator / Waninkoko to connect to the module DIP and redirect your calls so that we can simulate the reading of a DVD from a USB device, for example. The source code is semi-private and the original author does not want to go public (I am of the few who have), so devise a system that would adapt to different IOS and thus give the possibility to investigate on your own.
dip_plugin lodges in the memory address 0x1377E000 shared mload. That is, it hosts the final of the available memory.
dip_plugin need to connect to different functions of the module DIP, directly, so I resorted to a simple system that included a table with the addresses of the different routines.
By default dip_plugin has set his table for working with module DIP IOS36, so if we work with IOS38 DIP module for example (the current module, since the base IOS is 38), we must identify the module DIP tracking Starlet's memory in search of a familiar pattern.
Identifying the DIP module
For uLoader, is prepared to identify the DIP of IOS 36 and 38. What makes this form:
CÓDIGO: SELECCIONAR TODOmload_seek(0x20207c84, SEEK_SET); // dirección en IOS 38 de la cadena DIP a buscar
mload_read(patch_datas, 4); // lee 4 bytes sobre un buffer temporal alineado a 32 bytes
if(patch_datas[0]==0x6e657665)
{
is_ios=38; // es IOS 38
}
else
{
is_ios=36; // suponemos que es IOS 36
}
The address table:
The start of a dip_plugin address table consists with entries of 4 bytes, obviously
+0x00 DI_EmulateCmd -> dip_plugin (Thumb) entry function
+0x04 0x12340001 -> ID of dip_plugin
+0x08 dvd_read_controlling_data -> Data buffer address in dip_plugin (IOS 36: 0x2022DDAC IOS 38: 0x2022cdac)
+0x0c handle_di_cmd_reentry+1 -> Point of return to DIP (Thumb) (IOS 36: 0x20201010+1 IOS 38: 0x20200d38+1)
+0x10 addr_ios_shared_alloc_aligned+1 -> Function DIP (Thumb) (IOS 36: 0x20200b9c+1 IOS 38: 0x202008c4+1)
+0x14 addr_ios_shared_free+1 -> Function DIP (Thumb) (IOS 36: 0x20200b70+1 IOS 38: 0x20200898+1)
+0x18 addr_ios_memcpy+1 -> Function DIP (Thumb) (IOS 36: 0x20205dc0+1 IOS 38: 0x20205b80+1)
+0x1c addr_ios_fatal_di_error+1 -> Function DIP (Thumb) (IOS 36: 0x20200048+1 IOS 38: 0x20200048+1)
+0x20 addr_ios_doReadHashEncryptedState+1 -> Function DIP (Thumb) (IOS 36: 0x20202b4c+1 IOS 38: 0x20202874+1)
+0x24 addr_ios_printf+1 -> Function DIP (Thumb) (IOS 36: 0x20203934+1 IOS 38: 0x2020365c+1)
+0x28 Reserved
+0x2c Reserved
+0x30 Reserved
+0x34 Reserved
+0x38 Reserved
+0x3c Reserved
+0x40 in_ES_ioctlv -> entry point of ioctlv function of module dev/es (used with 'Skip IOS' in uLoader)
Connecting with dip_plugin module
The DIP module is patched in the installation process in order to prepare a jump in an area of shared memory access inside the module DIP, with a view to locating and easily divert the course of implementation of DIP to dip_plugin.
From uLoader this is done as follow:
IOS 36 wrotes:
memcpy(ios_36, dip_plugin, 4); // copy the entry_point (preserves entry point)
memcpy(dip_plugin, ios_36, 4*10); // copy the adresses from the array
mload_seek(0x1377E000, SEEK_SET); // copy dip_plugin in the starlet
mload_write(dip_plugin,size_dip_plugin);
// enables DIP plugin
mload_seek(0x20209040, SEEK_SET); // enables dip_plugin bypass
mload_write(ios_36, 4);
mload_close();
IOS 38 wrotes:
memcpy(ios_38, dip_plugin, 4); // copy the entry_point (preserves entry point)
memcpy(dip_plugin, ios_38, 4*10); // copy the adresses from the array
mload_seek(0x1377E000, SEEK_SET); // copy dip_plugin in the starlet
mload_write(dip_plugin,size_dip_plugin);
// enables DIP plugin
mload_seek(0x20208030, SEEK_SET); // enables dip_plugin bypass
mload_write(ios_38, 4);
mload_close();
As you can see, uLoader copy the address of the function dip_plugin input to a table, then copy the corresponding table on dip_plugin after dip_plugin hosts in the Starlet memory and finally connects dip_plugin copying the address of your input function in a particular point of shared memory in DIP (I'm afraid you'll have to look at the patch installer DIP, disassemble the module DIP (NUS Downloader lets you download the IOS decrypted) and compare the different directions to understand how it works exactly, if you want to adapt it to other IOS. If you're only how to use it, this will do)
The dev/es patch of ioctlv
From uLoader have an option called 'Skip IOS' and that is implemented, has little utility. Furthermore, it redirects dip_plugin saving and not have to add another module of similar cut.
The role is to capture the feature you are trying ioctlv in dev / es. If you have you lost an eye to libogc, you can look at es.c some features you can check by this detour.
From mload provide a series of patches that cause the diversion of this function to its returning crt0.s normally, if not program a function of treatment.
The way to do would be:
mload_set_ES_ioctlv_vector(in_ES_ioctlv); // thumb in_ES_ioctlv function
mload_close();
In the Starlet module, this function would be:
asm.s
CÓDIGO: SELECCIONAR TODO.align 2
.code 16
.global in_ES_ioctlv
.thumb_func
in_ES_ioctlv:
push {r2-r6}
push {lr}
bl ES_ioctlv+1
pop {r1}
pop {r2-r6}
bx r1
.global out_ES_ioctlv
.thumb_func
out_ES_ioctlv:
push {r4-r6,lr}
sub sp, sp, #0x20
ldr r5, [r0,#8]
add r1, r0, #0
ldr r3, = 0x201000D5
bx r3
es_ioctlv.c
CÓDIGO: SELECCIONAR TODOextern int in_ES_ioctlv(void *);
extern int out_ES_ioctlv(void *);
struct _ioctl{
void *data;
u32 len;
};
struct _ipcreq
{ //ipc struct size: 32
u32 cmd; //0
s32 result; //4
union { //8
s32 fd;
u32 req_cmd;
};
union {
struct {
char *filepath;
u32 mode;
} open;
struct {
void *data;
u32 len;
} read, write;
struct {
s32 where;
s32 whence;
} seek;
struct {
u32 ioctl;
void *buffer_in;
u32 len_in;
void *buffer_io;
u32 len_io;
} ioctl;
struct {
u32 ioctl;
u32 argcin;
u32 argcio;
struct _ioctl *argv;
} ioctlv;
u32 args[5];
};
} ATTRIBUTE_PACKED;
int ES_ioctlv(struct _ipcreq *dat )
{
int r;
u32 ios,version;
ios_sync_before_read(dat, sizeof(struct _ipcreq));
if(dat->ioctlv.ioctl==8) // reboot
{
ios_sync_before_read( (void *) dat->ioctlv.argv[0].data, dat->ioctlv.argv[0].len);
ios=*(((volatile u32 *)dat->ioctlv.argv[0].data)+1) ;
version=1;
ios_sync_before_read((void *) 0x3140,8);
*((volatile u32 *) 0x3140)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision
*((volatile u32 *) 0x3188)= ((ios)<<16) | (version & 65535); // write fake IOS version/revision
ios_sync_after_write((void *) 0x3140,4);
ios_sync_after_write((void *) 0x3188,4);
return 0;
}
r=out_ES_ioctlv(dat);
return r;
}
This is more or less, what happens when you select "Skip IOS" from uLoader. What you could include code in a different module and investigate other things (the truth, you could take out a huge party to function as a mimic IOS installed and do not have to run another in its place, or even carry out the charge of an IOS modified to convenience, but I leave that to others and certainly leads by working alot)
I just bring to light the subject and explain it
FIN
uLoader Thread
hilo_utilidad-uloader-v3-0c-ocarina-y-forzado-de-video-idioma_1217626#p1715667691
Algesat escribió:Alla te va la segunda parte, de aplicaciones avanzadas compañero Hermes, si necesitas traducir algo mas, considerame a tu disposicion.