[INFO] mload: la llave del starlet (cIOS 202/222/223)

Artículo pasado a la wiki. se deja el contenido original del mensaje a fin de que el creador pueda revisarlo y posteriormente eliminar esta parte del mensaje, para dejar solo el contenido wiki.

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 XD ).

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:

IOS

int 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ódulos

int 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 -> Error



int 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 memoria

int 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 error



int 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


Especiales

void * 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().

Memoria

int 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 Heap



void* 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 NULL



void 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 imprevistos



void 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 error



void 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, ...)



Dispositivos

int 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 error



int 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 dispositivo



int 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 asociado



int 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 error


int 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 error



int 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 error



int 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



Interrupciones

int 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 error



int 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 error



int 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 error



int os_thread_stop(int id);

Para la ejecución de un hilo.

id: ID del hilo

ret: <0 error



int 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


Log

void 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
Impresionante explicación sobre como funciona.

La verdad se agradece saber que es lo que quieres que llegue hacer, y si realmente funciona y/o llega a funcionar como dices, puede abrir muchas opciones futuras para el que esté interesado en programar para Wii...

Gracias Hermes por tu trabajo desinteresado hacia la Scene de la Wii [ok]
Segunda Parte: Usos Avanzados

Hasta ahora hemos visto lo suficiente para entender cómo podemos construir nuestros propios módulos, donde cargarlos y la forma de cargarlos mediante las herramientas que no proporciona mload. Pero queremos llegar aún más lejos: ya que hemos podido acceder al Starlet con la posibilidad de cargar nuestro propio código ¿por qué no contar con unas herramientas que nos permitan incluso modificar el sistema y saltar las limitaciones que nos impone?

Para entender lo siguiente de forma más completa, nos viene de perlas una información que se puede encontrar en wiibrew.org en lo referente al Starlet, concretamente éste par de enlaces:

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

Para abreviar, me referiré a ellos como PDF 1 y PDF 2, cuando quiera señalar algún dato importante.

También hay un enlace sobre la arquitectura en general: http://www.arm.com/miscPDFs/14128.pdf

Modos de ejecución y vectores de excepción

Nuestros módulos se ejecutan en Modo Usuario, en el cual no tenemos permisos para modificar otras zonas de memoria o modificar ciertos registros y por tanto, no podemos mas que observar el contenido y en ocasiones, ni eso y la única forma de poder hacer algo, es mediante las syscalls que disponemos... ¿pero realmente, es la única forma?

Obviamente, se hace necesario ganar dichos permisos, de hecho, las syscalls que os he descrito anteriormente, no son más que un vía para acceder a una excepción que se produce cuando el ARM intenta ejecutar una instrucción no definida (es decir, una instrucción máquina inexistente), lo que provoca un cambio de modo del procesador y un salto a un punto determinado de la memoria, donde si se dispone de algunos de los permisos que necesitamos para hacer algunas de las cosas interesantes que buscamos :)

En Wii la tabla de excepciones se localiza a partir de la dirección 0xFFFF0000 y según el PDF 2, página 70, estos son los vectores que contiene:


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



Como podemos observar, cada excepción entra en un determinado modo del procesador cuya principal diferencia es, aparte de contar con una serie de permisos que no se tienen en modo de usuario, contar con una serie de registros adicionales, para almacenar la pila o de retorno distintos, aparte de contar con otros registros de estado. Además de los modos que podéis ver ahí, también contamos con el Modo Sistema, que se apoya en los registros del Modo Usuario, aunque evidentemente, la diferencia es que contamos con permisos de los que no disponemos en el Modo Usuario.

Por cierto, algo importante que conviene saber, es que cada vez que se produce una excepción se deshabilitan las interrupciones en el registro CPSR.

Si queréis echarle un vistazo a la tabla de registros disponibles para los distintos modos, en el PDF 2, página 55 la podemos encontrar (modo ARM o 32 bits) y en la 57, la correspondiente al modo Thumb.

Por si no estáis familiarizados con el ARM (yo no es que sea un experto, que conste: mas bien tengo algunas nociones), los ARM cuentan con un modo de instrucciones completo, de 32 bits y un modo de instrucciones reducidas, llamado Thumb de tan solo 16 bits, para economizar memoria y que tiene limitado también el acceso a ciertos registros. El código que compilamos en C se convierte en instrucciones Thumb, mientras que el modo ARM de 32 Bits, queda reservado para operaciones especiales (como las llamadas a las syscalls) o rutinas que queramos optimizar en velocidad. En los PDF podéis encontrar información de los distintos modos de operación, si estáis interesados.

Por cierto, en el mismo PDF 2, página 58, podéis ver el registro de estado CPSR con la descripción de los distintos flags (Modo de ejecución, Thumb, Flags que deshabilitan IRQ y FIQ por software y los típicos flags de estado que tiene cualquier procesador).

El Club de la Lucha

"La primera regla del Club de la Lucha, es que no se habla del Club de la Lucha" ( [+risas] )

Bien, aquí es donde rompo esa primera regla para contaros mis aventuras y desventuras en el desarrollo del driver EHCI y trabajando con el sistema.

Parece obvio que si queremos obtener permisos, deberíamos tratar o bien de buscar algún tipo de exploit raro que nos plantase en Modo Sistema (porque o yo estoy torpe, o en Modo Usuario el registro CPSR pasa de ti cuando tratas de cambiar de modo, cosa que es lógica porque si no ¿para que coño sirven los permisos? [+risas] ) o mucho más fácil, ya que tenemos la posibilidad de crear nuestro cIOS, modificarlo en parte para que alguna de éstas excepciones que hemos visto, nos devolvieran el control.

Hace meses, le sugerí a Waninkoko que se podría utilizar las syscalls, ya fuera utilizando alguna "instrucción" que no se utilizara o modificar alguna de forma que pudiera cumplir con su cometido y además, nos proporcionase algún tipo de control, pero la verdad es que yo no estaba interesado en llevarlo a cabo personalmente y tenía otras preocupaciones.

Pero lamentablemente, el driver EHCI me daba muchos problemas de cuelgues raros que parecían debidos a problemas de sincronización con otros hilos o interrupciones en puntos incómodos (parece ser que es un bug en IOS 36, pero ¿como coño iba a suponer yo eso?) y cuando ya me había contenido como 100 veces de estampar la Wii contra la pared XD, fruto de la impotencia y la desesperación, pensé en mirar la forma de usar interrupciones.

Desensamblando a partir del vector de las syscalls, encuentras fácilmente la tabla de saltos de las funciones de tratamiento de dichas syscalls y en concreto la función correspondiente a os_software_IRQ(), que es la fallaba a la hora de fijar la IRQ 4.

Curiosamente, ésta función utiliza también una tabla de saltos para tratar las diferentes interrupciones y fue fácil darse cuenta que permite fijarlas a partir de IRQ 4 y que había una serie de interrupciones que utilizaban una rutina común de retorno...

Mirando la IRQ 4, vi que hacía un chequeo que si no se cumplía, no permitía fijar los flags correspondientes para dicha IRQ. Así que calculé el salto necesario para esquivar ese chequeo al mismo tiempo que adoptaba la IRQ 9, que estaba fuera de uso, para que saltara a un punto del crt0.s de mload, con el fin de obtener permisos (son los dos parcheos actuales que se hacen en el cIOS installer)

El resultado es que a pesar de que la IRQ 4 estaba habilitada, no funcionaba (porque me faltaba una información que provocó que creara éste hilo hilo_divagaciones-sobre-ehci-y-su-vector-de-interrupcion_1277800), pero la IRQ 9 si que funcionaba perfectamente, como mostraba el LED frontal encendido (es uno de los registros prohibidos en Modo Usuario).

Gracias a isobel supe lo que necesitaba para poder poner en marcha las interrupciones (un flag en uno de los registros EHCI reservados), pero ni se resolvían los problemas y además, tenía otros nuevos, debido a la posibilidad de que se produjeran varias interrupciones de golpe (sobre todo cuando pasé a utilizar mi propio vector de interrupciones).

Sin embargo, desensamblar programas produce que indirectamente, observes cosas que no vas buscando. Entre eso y los PDF, ya me había dado cuenta que el vector de SWI estaba fuera de uso, que aquí se llegaba con las interrupciones deshabilitadas, en diferente modo que con las syscalls y que podría utilizar una serie de instrucciones especiales propias para llamar a las diferentes rutinas. Sobre todo necesitaba acceder a las syscalls relacionadas con la caché y que esto no supusiera una complicación extrema.

La verdad es que recuerdo una conversación con marcan de hace mucho, sobre el lío que estaban suponiendo las funciones de caché (invalidate/flush) por que aparte de lo preceptivo en el ARM, había que tontear con una serie de registros desconocidos, de cuando él desarrollaba bootmii. De hecho, si le echáis un ojo a MINI podéis ver que llama a una función más rara que un perro verde, para cerrar dichas operaciones.

Así que una de mis ideas era ver si podía de alguna manera, llamar a las syscalls sin tener que recurrir a usar la tabla de syscalls para llamarlas de forma directa (que en cada IOS suele estar colocada en un sitio diferente) o tener que copiar parte de MINI... al menos para ese tipo de operaciones de caché (necesarias si vas a modificar cosas en caliente). Y eso hacía aún mas interesante trabajar con SWI en vez de con os_software_IRQ(9), que además, se cepillaba uno de los parámetros pasados como registros en dicha función. Además existía el atractivo de poder capturar mensajes del sistema, pues yo sabía del uso de svc 0xab en la función os_puts() de libcios y ya había visto referencias a ella en los módulos.

Total que ese fue el origen del vector de tratamiento de SWI, donde deposité parte de mi confianza, en la creencia de que si se deshabilitaba las interrupciones en determinados puntos críticos, tal vez se resolvieran los errores del driver EHCI tan extraños (lo mismo tenías un error al minuto, que lo tenías al cabo de una hora... y después cinco errores en un intervalo muy corto y luego otra hora y un bloqueo que requería desenchufar :-?. Raro de cojones )

Pronto me dí cuenta que no iba a ser posible utilizar las syscalls como syscalls (se pasa de Modo Indefinido a Modo Sistema y eso hace que por ejemplo, la pila no coincida, ni el retorno), e incluso que estando en "Modo Dios", que no podía acceder a determinados puntos de la memoria (por ejemplo, no podía parchear dev/es), aunque afortunadamente, el propio vector IRQ ya te mostraba como fijaba los permisos de Acceso en Modo Cliente para todos los dominios y los PDF contenían información sobre ello (PDF 1, página 47. En el crt0.s de mload, podeis ver dos funciones read_access_perm() y write_access_perm() relacionadas con ésto)

Al final tenía todo operativo, pero seguía teniendo un driver inestable... entonces se me ocurrió probar con IOS 38, dado que ya tenía su modulo DIP adaptado para funcionar con uLoader y joder, era muy raro que fuera un problema del driver y ya se me habían acabado las posibilidades (era eso, o golpear la Wii contra la pared, seguida por el disco duro XD) y..... EUREKA! ni un puñetero error de lectura y todo estable: ya solo quedaba limpiar el código de cosas que no fueran necesarias y tratar de añadir al nuevo servicio SWI todo lo que pudiera ser útil y necesario, no solo para trabajar con el driver EHCI o con uLoader, si no con vistas a un uso más profundo y general.

"la regla de oro del Club de la Lucha, es que si no te rindes, puede que no consigas lo que esperabas en un principio, pero también puedes obtener otras recompensas a cambio. A veces incluso, tienes algo de suerte y te llevas mucho más de lo que esperabas en un principio"

Gracias a eso, ahora tengo un driver muy estable, sin un solo error de lectura en mis unidades, trabajando por interrupciones y un conjunto de herramientas que me permiten un acceso pleno y la posibilidad de modificar los IOS en caliente (eso si: después de una buena carga de trabajo y poner en peligro mi estabilidad mental, la salud de la Wii, del disco duro y de la pared [+risas])

Los Servicios SWI de mload

El vector SWI es capturado y preparado para responder a la instrucción en ensamblador "svc x" (también se puede escribir como "swi x", si no me equivoco). Al llamar a la instrucción, se produce una excepción y es responsabilidad de la función de tratamiento de dicha excepción, buscar el código de instrucción y obtener el argumento "x". La instrucción svc existe también en modo thumb, pero no se si por limitación de la excepción o por algún problema, solo he conseguido que funcione si está alineada a 4 bytes. De todas formas, es preferible usar la versión de 32 bits, bajo mi punto de vista.

Por otro lado, conviene saber que las interrupciones deben seguir deshabilitadas por software y que se dispone una pila de 0x900 bytes para trabajar con las diferentes funciones SWI, de los cuales, se gastarán unos cuantos en guardar registros, etc, por lo que hay que tener especial cuidado en procurar que las variables locales, no ocupen mucho espacio.

Ah! y evidentemente... no se debe llamar a una función que hace uso de SWI dentro de una función SWI.

Los servicios de mload, se ofrecen en "svc 0xcc" y en libcios como ya comenté, se dispone de la librería swi_mload para facilitar el acceso a esos servicios. Evidentemente, solo se puede acceder a estas funciones desde el Starlet.

swi_mload

Estas son las funciones ofrecidas:

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'



Trabajando en Modo Supervisor

La función swi_mload_call_func() y la función swi_mload_add_handler() nos muestran las vías para entrar en Modo Supervisor. Una vez dentro, no podemos utilizar llamadas a syscall y tal vez necesitemos acceder a características especiales que si bien, yo no puedo proporcionar todos los datos, si puedo proporcionar una serie de funciones que pueden ser necesarias y útiles.

Leyendo y escribiendo los permisos de acceso

En el crt0.s de mload, podemos ver:


.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


Las funciones se definen como:

u32 read_access_perm(void);

Lee los permisos actuales.

void write_access_perm(u32 flags);

Fija los permisos



Y se puede tener acceso completo con write_access_perm(0xffffffff);. Evidentemente, conviene restablecer los permisos una vez finalizado lo que quiera que estemos haciendo (normalmente, esto solo se utiliza cuando toquemos algún área de memoria prohibida). La descripción de los permisos, se puede ver en PDF 1, página 47

Invalidar la caché de instrucciones

Cuando queramos hacer algún tipo de parche en el código en ejecución, nos puede interesar asegurarnos de que la caché de instrucciones se actualiza con los valores adecuados. Normalmente, podemos controlar la caché de datos, pero no se dispone de ninguna syscall que haga lo mismo con la caché de instrucciones por lo que debemos ocuparnos nosotros mismos. En el PDF 1, página 51 podemos ver las instrucciones del procesador necesarias

En crt0.s de mload podemos ver:

   .align 4
   .code 32
   .global ic_invalidate
ic_invalidate:
   mov      r0, #0
   mcr      p15, 0, r0, c7, c5, 0
   bx      lr


Y se define así:

void ic_invalidate(void);

Función que invalida toda la caché de instrucciones


El problema es que teóricamente, en Wii hay que hacer algo más para actualizar la caché, por lo que lo mejor sería es invocar ésta función y después, llamar a las instrucciones de caché de datos con las que contamos sobre la zona modificada, por si las moscas cojoneras.

Acceso a Syscalls

Pero ¿Con que funciones contamos para trabajar con la caché de datos, si no podemos llamar a las syscalls de forma directa?

Ahí es donde interviene la función swi_mload_get_syscall_base().

Si mirais el main.c y el crt0.s de ehcmodule, podeis ver que en el main.c se declara ésta variable:

u32 syscall_base;


y desde el main() se llama a dicha función de ésta forma (una sola vez):

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


Con esto ya hemos ajustado lo necesario para poder invocar syscalls de forma directa, utilizando su tabla de saltos. En el crt0.s podemos observar:

   .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



Como se puede observar, se utiliza el registro r11 para contener el índice a la syscall, mientras el registro r12 se utiliza para almacenar la dirección de la tabla y calcular el desplazamiento necesario para poder invocar a la función de syscall requerida.

Tenéis que tener en cuenta que las syscalls relacionadas con los hilos, seguramente fallen, al no estar trabajando en el contexto adecuado, pero realmente, no tenéis por que usarlas aquí.

Las syscalls que os he mostrado, se declaran así:


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()



El vector de interrupción

En crt0.s de ehcmodule, se puede observar la rutina interrupt_vector(). La rutina está ajustada para trabajar con IOS 36, pero como se puede observar en main.c en la función copy_int_vect() se llevan a cabo unos parches en crt0.s para poder ajustar la rutina para trabajar con IOS 36,37,38 y 60.

El modo de capturar el vector se realiza en la instrucción "tst r8, #0x1" del vector original, modificando esa instrucción y la siguiente para obtener un "ldr pc, =interrupt_vector" . Podéis ver en main.c "static u32 vector[2]={ 0xE51FF004, 0};" utilizada para llevar a cabo los parches necesarios (el segundo valor, sería la dirección de salto)

El registro r8, almacena los flags de interrupción, después de haber sido enmascarados con la máscara de interrupciones. Por tanto, un bit alzado significa una interrupción en curso.

El vector de interrupciones hace una serie de comprobaciones en cadena y entre ellas está la interrupción EHCI, que responde a la instrucción "tst r8, #0x10"

Por ello hacemos dicha comprobación y si no se ha producido interrupción, hacemos la comprobación "tst r8, #0x1" original (vector del timer) y saltamos al punto conveniente del vector original en virtud al resultado que nos de.

Si la interrupción EHCI está en proceso, la instrucción "bic r8, r8, #0x10" se encarga de que no se vuelva a tratar dicha interrupción más adelante, cuando estemos de vuelta en el vector original de interrupción.

Las intrucciones "mov r2, #0x10" y "str r2, [r7]" tienen el cometido de anular el flag de interrupción en el registro de interrupciones (registro 0x0d800038, podéis observar dichos registros en libcios, concretamente en starlet.h)

Después observamos una serie de instrucciones para preservar la pila, cambiarla por otra interna (de 0x200 bytes) y como preservamos una serie de registros, antes de saltar a _ehci_vector_ , desde la que se volverá a saltar a ehci_vector(), en modo thumb (ehci_vector() se declara en ehci_interrupt.c)

A la vuelta se hace un test con el retorno: si se retorna 1, se llamara a la función int_send_device_message(4) después de haber restablecido la pila, (opción que ahora mismo no se utiliza ya que se invoca a dicha función desde el propio ehci_vector()) pasando después a tratar "tst r8, #0x1", (la interrupción del timer) y retornando en consecuencias al vector original

La función int_send_device_message() tiene el cometido de enviar un mensaje a la cola asociada al dispositivo, si éste se registro con la syscall os_register_event_handler(). En ehcmodule, cuando estamos transfiriendo datos, el hilo ajusta un timer (timeout) y aguarda a que la cola asociada reciba un mensaje, ya sea desde la interrupción o del timer.

Espero que con éstas notas, sepáis entender como trabaja el vector de interrupciones. Obviamente, se hace necesario saber algo de ensamblador de ARM, disponer de un buen desensamblador (como IDA Pro) y hacer lo propio partiendo desde la tabla de excepciones que os puse arriba, y en concreto, desde el vector IRQ para tener una vista más apropiada del tema.

DIP Plugin (cIOS 222/223)

dip_plugin es un módulo binario (no usa formato .elf) desarrollado por Wiigator / Waninkoko para conectar con el módulo DIP y redirigir sus llamadas de forma que podamos simular la lectura de un DVD desde un dispositivo USB, por ejemplo. El código fuente es semi-privado y el autor original no quiere hacerlo público (soy de los pocos que disponen de el), por lo que idee un sistema que permitiera adaptarlo a los diferentes IOS y así daros la posibilidad de poder investigar por vuestra cuenta.

dip_plugin se aloja en la dirección 0x1377E000 de la memoria compartida de mload. Es decir, se aloja al final de la memoria disponible.

dip_plugin necesita conectar con diferentes funciones del módulo DIP, de forma directa, así que recurrí a un sencillo sistema que incluía una tabla con las direcciones de las diferentes rutinas.

Por defecto dip_plugin tiene fijada su tabla para trabajar con el módulo DIP de IOS36, así pues si queremos trabajar con el módulo DIP de IOS38 por ejemplo (el módulo actual, pues el IOS base es el 38), tenemos que identificar el módulo DIP rastreando en la memoria del Starlet en búsqueda de un patrón conocido.

Identificando el módulo DIP

En el caso de uLoader, está preparado para identificar el DIP de IOS 36 y 38. Lo hace de ésta forma:

   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
      }


La tabla de direcciones

El inicio de dip_plugin se compone de una tabla de direcciones con entradas de 4 bytes, obviamente


+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)



Conectando el módulo dip_plugin

El módulo DIP es parcheado en el proceso de instalación con el fin de preparar un salto en una zona de memoria con acceso compartido dentro del propio modulo DIP, con el propósito de poder localizar y desviar fácilmente el curso de ejecución de DIP hacia dip_plugin.

Desde uLoader ésto se hace así:

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();


Como se puede apreciar, uLoader copia la dirección de la función de entrada de dip_plugin a una tabla, luego copia la tabla correspondiente sobre dip_plugin, después aloja dip_plugin en la memoria del Starlet y por último conecta dip_plugin copiando la dirección de su función de entrada en un punto determinado de memoria compartida en DIP (me temo que tendrás que mirar los parches DIP del instalador, desensamblar el módulo DIP (NUS Downloader te permite bajar los IOS desencriptados) y comparar las diferentes direcciones para comprender como trabaja exactamente, si quieres adaptarlo a otros IOS. Si solo buscas la forma de utilizarlo, esto servirá)

El parche dev/es de ioctlv

Desde uLoader tenemos una opción llamada 'Skip IOS' que así como está implementada, tiene poca utilidad. Además, se redirige hacia dip_plugin para economizar y no tener que añadir otro módulo de corte similar.

La función consiste en capturar la función que trata ioctlv en dev/es. Si le echáis un ojo a libogc, podéis observar en es.c algunas funciones que podréis controlar haciendo éste desvío.

Desde mload se hacen una serie de parches que provocan el desvío de ésta función a su crt0.s retornando normlamente, si no se programa una función de tratamiento.

La forma de hacerlo seria:

mload_set_ES_ioctlv_vector(in_ES_ioctlv); // thumb in_ES_ioctlv function
mload_close();


En el módulo del Starlet, ésta función sería:

asm.s

   .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

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;
}



Esto es mas o menos, lo que sucede cuando se activa "Skip IOS" desde uLoader. El código lo podríais incluir en un modulo diferente e investigar otras cosas (la verdad, se le podría sacar un partido enorme a la función, como simular la presencia de IOS que no tenemos instalados y hacer que se ejecuten otros en su lugar, o incluso llevar a cabo la carga de un IOS modificado a conveniencia, aunque eso lo dejo para otros y seguro que lleva muucho trabajo de por medio)

Yo simplemente, me limito a sacar a la luz el tema y explicarlo un poco [+risas]

FIN

Hilo de uLoader

hilo_utilidad-uloader-v3-0c-ocarina-y-forzado-de-video-idioma_1217626#p1715667691
¡Grande Hermes, Grande!

No dejas de sorprender a toda la comunidad con tus avances, programaciones y/o mejoras. Macho, que alegría me das con todo lo que puedo leer aquí y lo que llevo aprendido en el añito que llevo así un poco más decentemente con todos vosotros...

Un saludo,
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
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


Hay que especular con el espacio, porque aparte de que no puedo doble-postear, no tengo forma de controlar el hilo y se que la información va a ser muy extensa... y siempre es mejor que continué poco después a que lo haga dentro de 3 páginas ;) (hay que ser previsor XD )
Hermes, no dejas de impresionarme. [sonrisa]
Mientras los sceners van cayendo, tu te alzas y continúas la marcha.

Y sin duda no puedes controlar lo que postea la gente. De hecho, me estoy arrepintiendo ahora mismo de postear.
De todos modos, siempre puedes reabrir el hilo en otro creado especialmente por los mods que esté cerrado hasta que termines.

Saludos, sigue así que estoy metiéndome de lleno en tu curso de programación. [beer]
vagomayor está baneado por "troll"
Me encanta tu maestria Hermes muy informativo.
No se lo que has dicho, pero tu madre por si acaso [+risas]

Nah, ahora en serio, tiene una pinta de interesante que tira pa tras, pero como no me entero muy bien...

El caso, buen trabajo [ok] [oki]
Muy interesante.. Aunque no lo entienda muy bien, creo que he entendido algo: esto permitiría parchear un IOS en memoria y de esa manera no tener que andar instalando cIOS cada vez que se quiera modificar algo??
siempre puedes usar el Wiki :P

buen trabajo [oki]
Hermes, eres grande... no sólo has construido una estructura sólida para el Homebrew, sino que compartes con todos tu conocimiento, experiencia y trabajo.
Mil gracias
Con todo esto quieres decir que el parcheo sería selectivo y en tiempo real?
Es decir...
  • Juego A - cargo cIOS 202 (Desde la NAND)
  • Juego B - cargo cIOS 222 (Desde la sd)
  • Juego C - cargo cIOS 223 (Desde el Pdrive)
en un espacio temporal X¿? digamos hipoteticamente un slot entre ios 36 e ios 57 que estuviese inutilizado por ioses legitimos del firmware? ^^
Corrigeme si escribí alguna tonteria, etc..
Saludos! :p
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?

Una cosa hermes, deberías poner los fuentes del mload aquí, aunque ya se que se pueden sacar del uloader.

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
croatoan escribió:Con todo esto quieres decir que el parcheo sería selectivo y en tiempo real?
Es decir...
  • Juego A - cargo cIOS 202 (Desde la NAND)
  • Juego B - cargo cIOS 222 (Desde la sd)
  • Juego C - cargo cIOS 223 (Desde el Pdrive)
en un espacio temporal X¿? digamos hipoteticamente un slot entre ios 36 e ios 57 que estuviese inutilizado por ioses legitimos del firmware? ^^
Corrigeme si escribí alguna tonteria, etc..
Saludos! :p

Perdoname Hermes por las simplificaciones e inexactitudes pero asi lo entendemos todos XD.

En la jui va todo en uno. Cada IOS contiene todos los drivers. Para el mando, para la guifi, para la SD... Si sacan un mando un microfono o una camara no pueden meter el driver y ya esta. Necesitan actualizar el IOS o meter otro nuevo con el driver ya incluido.

Hermes lo que ha hecho es un IOS especial. Cuando se carga deja un pequeño hueco en la RAM exclusiva para IOS. Cuando se ejecuta el uLoader lo primero que hace es pasarle sobre la marcha el driver para que el ios lo meta en el hueco. El driver USB esta DENTRO del DOL del programa no en el ios.

Esto es un ventajon inmenso. Los programadores se ahorran muchisimo trabajo al no tener que hacer cIOS. Mientras se ajusten al hueco pueden personalizar el IOS con los drivers que les de la gana y poder no solo poner un driver sino incluso cambiarlo por otro en medio del programa. Para los usuarios mucho mejor porque en la NAND el IOS se guarda sin drivers caseros y no hay que reinstalar un cios con el que nos podemos cargar la consola.
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?


Hay que amenazar de muerte a los mods XD

rodries escribió:Una cosa hermes, deberías poner los fuentes del mload aquí, aunque ya se que se pueden sacar del uloader.


Tranquilo que todavía queda muuucho por explicar. Luego, separaré los fuentes y los colocaré aquí con las etiquetas corregidas (de momento, esto es un borrador)


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


Esperad que acabe primero, no vaya a ser que complemente la información (vosotros mismos podéis sugerir que aclare algo) y luego toque volver atrás.

Vrsquid escribió:...


En efecto, la virtud de mload es poder hacer lo que comentas, pero desde la última versión, tenemos la capacidad de poder modificar el IOS en caliente, cosa que antes no era posible porque no disponíamos de los permisos necesarios. Y disponemos de mayor potencial desde el lado del PPC, pero desde el Starlet se dispone de nuevas herramientas y nuevas posibilidades con las que antes no se contaban, que son bastante interesantes.

En el primer post estoy agrupando la parte digamos, normal, explicando la herramientas de mload desde el lado PPC para cargar módulos, etc y luego explicando lo que conozco de las syscalls, para que podáis "armar" vuestros propios módulos. En los fuentes de mload hay ejemplos y los propios módulos mload y ehcmodule muestran como trabajar con dispositivos. Quizá yo comente algo al respecto de forma práctica.

En el segundo post que tengo reservado, me gustaría explicar las herramientas avanzadas como los servicios SWI de mload y hablar algo sobre los modos del procesador. En realidad, todo lo que vuelque aquí va a depender de lo que tarde en cansarme [+risas] (la verdad es que es cansado tratar de volcar lo que uno tiene en la mollera y tratar de que la gente asimile lo que tratas de explicar sin que les salte una excepción [+risas] )

Hoy no se si retomaré el tema: ahora mismos estoy preparando el papeo, tengo sueño y estoy hasta el nabo de esperar en la cola del paro (cawen sus muelas: para darme 3 puñeteras fotocopias de mierda y pedirme dos gilipolleces, 3 horas y media de pié haciendo cola [enfado1]. Y eso que no he sido de los últimos en llegar, precisamente ...)

El caso es que hoy la siesta no la perdono XD
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 ;)
CHINCHETA YA!!!!!!!!!!!!!!!!
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 ;)


Okis, ya te pediré los cambios, aunque no creas que tengo yo muchas ganas de continuar con el tema: mas bien, la idea de éste hilo es transmitirlo todo, que al fin y al cabo, yo ya estaba retirado hace tiempo (tu te vas, pero yo soy al revés: estoy fuera, pero tengo temporadas en las que vuelvo XD).

Por cierto que hoy he estado bastante ocupado primero arreglando papeles, luego trabajando en casa y a eso de las 6 pm, me piro a comprar, para ver si puedo retomar el tema después y cuando vuelvo, estaba cayendo el diluvio (he vuelto empapado) ... pues bien, me encuentro que no tengo luz en casa, cosa que no me ha extrañado nada porque están levantando mi calle mejorando el tendido electrico o yo que se (el caso es que es de la Unión Penosa el tema). Pues bien, la luz no viene, la luz no viene, hasta que me he dado cuenta que era yo solo el que tenía el problema, que al parecer se ha jodido la instalación desde el poste que hay en mi casa al cajetin... y hasta hace unos 50 minutos no he tenido luz.

Está claro que me ha mirado un tuerto: encima me duele una muela y el dolor se me pasa al oido [enfado1] : ya solo falta que me parta un rayo, joder!

Que se pare el mundo que yo me bajo, que estoy hasta los cojones ya [+furioso]
Pues qué quieres que te diga, cada vez que te da por reaparecer no veas las alegrías que das. Ánimo, que lo que necesitas (al igual que yo), es dormir un poco...

Esperamos con muchas ganas tus noticias sobre el tema.

Un saludo,
Hermer, me siento orgulloso de ti. En plan papi y todo, no tiene sentido, pero es así.
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 XD



Nooooo por Dios XD. Gran trabajo Hermes como siempre,chincheta al canto por peticion popular. Seria muy interesante portarlo todo al wiki cuando este terminado!

Si necesitas cualquier cosa en el post dame un toque
Buen comienzo para empezar el dia XD
Parece ser que mload es un gran avance para la scene, pero hay algunas cosas que no acabo de entender. [+risas]
¿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!
jamonazo2000 escribió:Nooooo por Dios XD. Gran trabajo Hermes como siempre,chincheta al canto por peticion popular. Seria muy interesante portarlo todo al wiki cuando este terminado!


Yo el formato del wiki no lo manejo y además, creo que volcando todo esto en forma de post, ya he cumplido [+risas]. Si otro quiere subirlo ahí, pues me parece bien: así si algún día éste hilo se desliga, la información no se pierde en los océanos (si es que a veces, parece que necesito un foro para mí solo de tantas cosas que hay en mi haber [+risas] )

jamonazo2000 escribió:Si necesitas cualquier cosa en el post dame un toque


Hombre, tengo un post salteado que es donde irá la información avanzada: si lo puedes poner a continuación del primero,e staría bien.

enrikote escribió:Parece ser que mload es un gran avance para la scene, pero hay algunas cosas que no acabo de entender. [+risas]
¿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!


mload es un módulo que está dentro de los cIOS 202/222 y 223 que dota de la posibilidad a los sceners de poder añadir sus propios módulos sin necesidad de crear un nuevo cIOS y además, está dotado de herramientas que permiten modificar el cIOS en caliente.

Digamos que los sceners tienen los IOS de siempre, sobre los que se ha construido casi toda la scene de Wii, luego tienen MINI, que es una ruptura total, pero donde cuentas con toda la libertad para trabajar con el Starlet, con el único impedimento de cosas que se desconozcan y luego cuentas con los cIOS basados en mload, que toman en mi opinión, lo mejor de ambos, dado que no rompen con lo ya establecido y conocido por muchos y te brinda la oportunidad de poder añadir cosas nuevas y un acceso que hasta ahora, solo era posible con MINI o para tareas muy concretas (como por ejemplo, el cIOS 249 de Waninkoko)

Además, lo interesante de esta forma de trabajar, es que suministra un marco común, pero permite una completa libertad de trabajo: por ejemplo, mi módulo EHCI y el de rodries, son diferentes porque cada cual tiene unas necesidades (el tiene necesidad de poder reemplazar el dispositivo USB en un momento dado con Mplayer, mientras que yo tengo que hacer lo que no está en los escritos para trabajar con el mismo SIEMPRE y hacer que no se retornen errores desde uLoader, si no que internamente, se "arreglen" lo mejor posible, ya que si en un juego aparece un error de lectura, te dice que apagues la consola y se acabó el viciar [+risas] )

Lo raro del tema, es que haya tenido que ser yo el que tuviera la idea de hacer algo como mload [+risas] (coño, si yo pasaba del Starlet como de comer mierda y no quería saber nada de todo esto...)

Saludos.

PD: La primera parte de mload, creo que está completa. Si alguien ve algo raro que debiera corregir, que me de un toque [+risas] . Ya me pondré con la segunda parte en otro momento ;)
Gracias por tu respuesta Hermes, mas claro imposible [oki] .
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 [+risas] . 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ó:
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 [+risas] . 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 :)


Pues ya he colgado parte de la segunda parte, que tal vez te sirva para comprender algunas cosas.

Cuando pueda, pasaré a enumerar las funciones SWI de mload, aportando mas detalles y alguna de las funciones especiales necesarias.
Actualizados los fuentes de mload a la versión 3.0C:

- Añadida una pequeña corrección en el timeout del USB_SetConfiguration() (hay dispositivos que tardan más de un segundo en hacerlo)

- Añadido el módulo binario dip_plugin.

EDITO:

Tema finiquitado. Saludos ;)
Muchas gracias por tu brutal trabajo Hermes.
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 :)
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 :)


Puestos a que toque la loteria, que me toque a mí que ni siquiera tengo trabajo [+risas]
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?
XD
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?
XD


Aquí tienes/Here you are :cool: . 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.
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?
XD


Aquí tienes/Here you are :cool: . 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.


No me refería a los cambios... si no a la totalidad de la info [+risas]

Saludos
No me había interesado para nada esto del mload. Había escuchado de ello pero solo al hacer referencia de la instalación de los cIOS22 y 223 para solucionar alguna carga de backups.
Pero ahora que entré a probar el rollo de la carga de backups desde FAT con el UsbLoaderCFG, me di cuenta que por más que intentaba cargar los backups con el tradicional (y no menos grandioso) cIOS 32 v14 o 15, no podía. Estuve batalla y batalla con ello, hasta que probe con el mload, y se pudo SIN NINGÚN PROBLEMA. [360º]
No soy un experto en programación (aunque se algo de informática), y no sé bien como funciona esto que explica al inicio HERMES, pero lo que si puedo asegurar, es que es una GRAN pero GRAN APORTACIÓN a la scene.
HERMES: Gracias de verdad por esta super aplicación. Creo que apenas es la punta del iceberg lo que se está comenzando a explotar. No dejes de seguir con el desarrollo y mejoras. [plas]
Que Grande, hacia meses que no pasaba por el foro y me encuentro con este super post de Hermes. Habria que hacerte un monumento a la Scene, gracias a gente como tu esto avanza hacia nuevas fronteras.

Gracias por tu transmitirnos tu saber artista [beer]
Hermes escribió: Además, nosotros no sabemos casi nada y tenemos que avanzar a tientas probando cosas.


eso si es ser humilde xD.

no entiendo nada de nada. pero gracias
Muy grande esos avances!!

En Programación Siempre Se Ha Dicho Que Como Mejor Se Aprende Es Derribando A Cabezazos Los Muros, Así Que, Ánimo Y Muchas Aspirinas!! JeJe
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?
XD

Bueno, como veo durante semanas que nadie se atreve a poner una traduccion al ingles de la info, aqui te va la mia, algo casera pero al menos lo entenderan los angloparlantes al leerlo, que al fin y al cabo, es de lo que se trata.

Alla va el tocho, la segunda parte ya te la traducire en cuanto saque un poco de tiempo:
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
Hola :)

Siento no haberme dado cuenta antes de que te habías tomado la molestia de traducir el tutorial.

Ahora mismo, estoy bastante petado y como se puede observar, me paso poco por aquí, el link de descarga está caído (en realidad se puede descargar desde el enlace de uLoader la versión posterior), pero tengo una versión nueva de los módulos, que espero publicar pronto (la nueva versión de uLoader tendrá muchas novedades y eso requiere tiempo que sale del que dispongo para visitar los foros, por ejemplo XD)

Saludos y gracias por colaborar ;)


Edito

He actualizado el enlace de descarga con la version 3.5.

El instalador es el mismo (v4) y no se ha tocado nada de mload, obviamente.

Pero en ehcmodule si que hay cambios, los mas importantes:

- la limitación interna a 64KB de lectura/escritura por cada paso (creo que hay dispositivos que funcionan mal, si les pasas un tamaño de lectura mayor, pero es una limitacion razonable en velocidad y que no significa que no le puedas pedir un bloque mucho mayor, si no que las operaciones se reducen internamente a un máximo de 64KB)

- La configuración de ehcmodule: en el crt0.s se ha incluido una pequeña tabla que empieza por la cadena "EHC_CFG", seguido de la signatura 0x12, 0x34, 0x00, 0x01 y de cuatro bytes que permiten:

-byte 0: si se fija a 1, ehcmodule trabajará con el puerto 1 en vez del puerto 0

-byte 1: si se fija a 1, se activa el Reset Bulk (hay dispositivos que son incompatibles con el... pero otros podrían necesitarlo)

-byte 2: si se fija a 1 fuerza la llamada de GetMaxLun en primer lugar (si no, se trata de montar LUN 0 y si no se consigue, se llama a ésta función y se trata nuevamente de montar LUN 0. Es posible que algún dispositivo se bloquee y necesite forzar el GetMaxLun)

- byte 3: si se fija a 1, incrementa los tiempos de Timeout de ciertas partes que podrían dar problemas.

- Se han aumentado los timeouts en ciertas partes. La razón es muy sencilla: al desaparecer los continuos fallos de lectura que teníamos antes, es mejor tener una demora en caso de que se produzca un error que yo no he vuelto a detectar... que andar corto de tiempo en ciertas unidades.

- Además, como quizá sepais ya por uLoader, se ha añadido soporte para unidades DVD externas. Por razones de uLoader, me he centrado en los checks para discos que utilizan 2048 bytes por sector, pero eso es fácilmente modificable (el buffer del Watchdog está preparado hasta para 4096 bytes y cambia la estrategia de lectura)

- Se ha añadido la rutina necesaria para soporte .CISO desde uLoader, para el modo DVD USB

- Se ha añadido un parche que captura la llamada de la syscall os_message_queue_receive() en dev/di que es interesante porque muestra de que forma podemos meter una rutina similar justo antes de cualquier acceso a dispositivo y adulterar el mensaje que recibe [+risas]

- Tambien se ha añadido un vector de servicio SWI a ehcmodule, que se puede llamar con la instrucción "svc 0xcd". Lo que lo hace interesante, es que puede permitir un acceso directo a las funciones de lectura y escritura de sector, pero sobre todo, que te devuelve el id del heap utilizado para operar con WBFS, que por ejemplo, no se usa para homebrew. Y son 136KB que están ahí tocandose el nardo y que vendría bien para otros trabajillos (en los fuentes uLoader he incluido un modulo (fat-module) para trabajar con FAT/FAT32 desde SD y USB con algunos arreglos, que utiliza toda ésta memoria para su propia caché)

Entre la descarga de uLoader, podeis encontrar un instalador de cIOS para usar el puerto 1, que me pidieron usuarios que no podían hacer uso del puerto 0 por alguna razón. Sin embargo, aunque yo no estoy seguro de su funcionamiento sin problemas, por carecer de periféricos de Instrumentos USB y por eso incluyo ese cIOS, desde mload se pueden acceder a los registros EHCI y modificarlos a mano para que fuercen modo USB 1.1 en el puerto 0 y liberen el puerto 1 para operaciones USB 2.0 (luego todo dependerá de si tiene un dispositivo pijotero o no). Se supone que desde Homebrew no debería dar problema y por tanto el cIOS para puerto 0 debe ser válido para trabajar con puerto 1 sin tener que desenchufar el dispositivo, usando éste método:

El código es el siguiente, tal como lo muestro en uLoader (hay que parchear antes de cargar ehcmodule):

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);
   }


Y es que mload tiene una caja de herramientas muy útil [+risas]

Saludos
Alla te va la segunda parte, de aplicaciones avanzadas compañero Hermes, si necesitas traducir algo mas, considerame a tu disposicion.

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 TODO
   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
      }


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 TODO
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;
}


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


Un saludo artista, espero que te sea util.
Algesat escribió:Alla te va la segunda parte, de aplicaciones avanzadas compañero Hermes, si necesitas traducir algo mas, considerame a tu disposicion.


Buen trabajo :)

Ahora solo queda que yo encuentre algo de tiempo y ganas para actualizar lo referente a dip_plugin , darle formato a tu traducción y organizarlo con el Moderador para que los post vayan arriba, que últimamente estoy demasiado saturado y hasta los cojones de tó y no doy abasto [+risas]
Pss yo tambien como que no entendi mucho y valla que ya lei todo.
como sea [ok] FELICIDADES [ok]
por este gran trabajo y por tanta paciencia para escribir y compartir todo esto

????? y en que trabajas ????????
Si hay alguien que trabaje en esto contigo, que suerte tiene. Es muy interesante.
Gracias por tu trabajo.
Tengo 2 preguntas...

1. Cuando instalo la IOS222, al abrir cualquier aplicación (uloader, backup launcher, etc) se me queda la pantalla en negro, se apagan las luces del mando y ya no responde la consola. Si lo desinstalo, se pone todo normal, pero al abrir el uloader me pide la IOS222. ¿Que puedo hacer?

2. Las instrucciones esas de código que he visto, ¿son de c++?
45 respuestas