
Sistema Operativo QRV
QRV es una adaptación y reimplementación desde cero del sistema operativo QNX Neutrino 6.4 para hardware moderno de 64 bits, con RISC-V (rv64g) como arquitectura principal y x86-64 como objetivo secundario. El proyecto comenzó la Nochebuena de 2020. El nombre QRV evita deliberadamente cualquier asociación con la marca registrada QNX.
QRV es un sistema operativo completo, no solo un núcleo. El micronúcleo es su corazón, pero la mayor parte del trabajo se ha invertido en todo lo que lo rodea. Lo más notable es taskman, el gestor de procesos/memoria/rutas en modo de usuario (el procnto de QNX), que ha sido profundamente rediseñado y extraído del núcleo al espacio de usuario; junto con él, la biblioteca C, los controladores de dispositivo, el sistema de archivos, el cargador dinámico y los servidores del sistema han sido todos portados, limpiados para 64 bits y, en muchos lugares, reescritos sustancialmente. El micronúcleo es pequeño por diseño; el sistema operativo que lo rodea es donde reside la mayor parte de QRV.
Esto no es un fork que simplemente compila código antiguo con un nuevo compilador. Es un puerto cuidadoso, módulo por módulo, a un modelo LP64 real, con el límite propietario de procnto desmantelado, la maquinaria IFS/startup reemplazada y — a partir de las versiones más recientes — el Gran Candado del Kernel del núcleo eliminado por completo y el gestor de procesos/memoria/rutas movido fuera del núcleo a un servidor en modo de usuario.
Este README describe QRV v0.43.
El blog de desarrollo, con la historia completa del puerto, está en https://r-tty.blogspot.com. Una narrativa del tamaño de un libro, La Historia del Puerto QRV, reside en el árbol fuente bajo doc/tex/PortingStory/.
QRV se ha desarrollado en estrecha colaboración con Claude Code, la herramienta de codificación agente de Anthropic — gran parte del puerto, la depuración SMP y la documentación (incluyendo este README) se llevó a cabo como un esfuerzo de programación en pareja humano-IA, trabajando junto al autor.
obtain_proj.sh y el árbol os/TM_PRIVdevb-nvme y fs-qrvQNX es un sistema operativo de tiempo real basado en micronúcleo cuya idea definitoria es el paso de mensajes síncrono. En QNX, el núcleo en sí es minúsculo — sabe programar hilos, pasar mensajes, entregar señales, manejar temporizadores e interrupciones, y muy poco más. Todo lo que un sistema operativo monolítico pondría dentro del núcleo — el gestor de procesos, el gestor de memoria, el sistema de archivos, los controladores de dispositivo, la pila de red — se ejecuta en procesos de usuario ordinarios llamados gestores de recursos, y se comunican entre sí y con sus clientes a través de la misma primitiva IPC de enviar / recibir / responder.
Esta arquitectura es lo que hace elegante a QNX, y es exactamente lo que QRV preserva. Un programa que quiere abrir un archivo envía un mensaje; el servidor del sistema de archivos lo recibe, hace el trabajo y responde. El núcleo solo gestiona la cita. El resultado es un sistema donde un controlador puede fallar y reiniciarse sin derribar el núcleo, donde la base de computación de confianza se mide en decenas de kilobytes, y donde el límite entre "núcleo" y "aplicación" es un mensaje, no un muro de privilegios lleno de llamadas al sistema.
QRV toma las fuentes comunitarias de QNX Neutrino 6.4 de 2009 y trae ese diseño hacia adelante:
int/uint32_t/pid_t sobre punteros con las que el código de 32 bits está plagado.qemu-system-riscv64 (la máquina virt) y la placa de desarrollo SiFive Unmatched (FU740). x86-64 se mantiene compilable como verificación de portabilidad.mkifs (QRV usa el formato estándar CPIO); no hay una división separada startup/núcleo (startup está enlazado directamente con el núcleo); no hay callouts ni mini-controladores.Kconfig al estilo Linux, un enlace incremental del núcleo (los módulos se añaden y prueban uno a uno, no se lanzan al enlazador como un monolito de 32→64), y una cadena de herramientas de compilación cruzada (riscv64-linux-gnu-gcc).procnto es taskman (el Gestor de Tareas) en todas partes; todas las referencias a "Neutrino" se eliminan.QRV no implementa fork() (los programas se inician mediante posix_spawn()), y no tiene paginación bajo demanda ni intercambio — las mismas decisiones que QNX tomó en su generación 8.0.
QRV se rige por dos licencias a la vez, y entender cuál es cuál es esencial antes de construir o redistribuir cualquier cosa.
El código propio de QRV tiene licencia Apache 2.0. Todo lo escrito desde cero para este proyecto — el puerto a RISC-V, el nuevo sistema de construcción, la división de taskman en modo de usuario, la reelaboración del núcleo sin candados, los controladores y herramientas que hemos creado — es Apache 2.0. El texto completo está en LICENSE.txt.
El código derivado de QNX tiene la Licencia Comunitaria BlackBerry QNX (QCL) 2.0. Las partes de QRV que descienden de las fuentes comunitarias de QNX Neutrino de 2009 permanecen bajo la QCL, que permite el uso no comercial y académico de las fuentes derivadas. QRV no puede, y no tiene la intención de, relicenciar el código de QNX.
Esta realidad de doble licencia es precisamente la razón por la que este repositorio no contiene un árbol de fuentes listo para compilar. No tenemos permiso para redistribuir las fuentes derivadas de QNX. Por lo tanto, en lugar de enviar el código, este repositorio envía una receta (vea la siguiente sección): un mapa de dónde va cada archivo de QNX, más los parches de QRV que lo transforman. Usted obtiene las fuentes comunitarias de QNX ascendentes por su cuenta, desde su espejo público, y la receta reconstruye el árbol de QRV en su máquina. Su copia es suya; nosotros redistribuimos solo nuestros propios parches y metadatos con licencia Apache.
QRV también incorpora código bajo otras licencias permisivas — por ejemplo, componentes con licencia BSD adoptados de FreeBSD (reemplazando módulos obsoletos de QNX), un controlador de bloque virtio con licencia MIT de ascendencia xv6, y el shell Korn de MirBSD (mksh) como shell del sistema. WHAT_IS_WHAT.md es el desglose autoritativo, componente por componente, de qué está bajo qué licencia y de dónde proviene — consúltelo siempre que no esté seguro sobre un archivo o subsistema en particular.
Finalmente, el repositorio contiene PETITION.md: una solicitud abierta a QNX Software Systems y BlackBerry para que relicencien las fuentes históricas de Neutrino 2007–2009 bajo una licencia permisiva aprobada por la OSI. Si desea que los fundamentos de este trabajo sean algún día completamente libres, ese documento es el lugar para añadir su nombre.
obtain_proj.sh y el árbol os/Debido a que las fuentes derivadas de QNX no pueden redistribuirse aquí, este repositorio es una distribución de reconstrucción de fuentes. Contiene:
$ ./obtain_proj.sh
1. **Clona el espejo ascendente de la comunidad QNX** (`github.com/vocho/openqnx`,
un clon superficial).
2. **Coloca los archivos** según `placement.txt`, copiando cada archivo
ascendente a su ubicación QRV bajo `os/`. Informa cuántos archivos se
colocaron, ya estaban presentes o faltaban.
3. **Elimina el clon** una vez que se completa la colocación.
4. **Aplica la serie de parches QRV** de `patches/series`, en orden. Cada
parche está comprimido con LZ4 (`*.patch.lz4`) y se aplica con
`lz4cat … | patch -p1`. Los parches llevan la versión de la
versión actual.
5. **Establece permisos de ejecución** en los pocos scripts que los necesitan
(por ejemplo, `emu.sh`, `host_tools/mkgpt.py`).
Las herramientas requeridas son `git`, `patch` y `lz4cat` (del paquete
`lz4`); el script las verifica de antemano y te indica cómo instalar
cualquiera que falte.
El producto final es **`os/`** — un árbol fuente QRV completo y compilable:```
os/
├── kernel/ Everything linked into the qrv-kernel binary
│ ├── arch/riscv/ RISC-V port: vectors, traps, SBI, context switch,
│ │ ├── startup/ arch-specific startup (head.S, mmu.c, …)
│ │ ├── platform/ qemu_virt/, unmatched/
│ │ └── include/ context.h, cpu_paging.h, sbi.h, …
│ ├── startup/ arch-independent startup (hardware_init, smp, …)
│ ├── nano/ core nanokernel: messaging, scheduling, sync, xfer
│ ├── kext/ kernel extensions (kerexts)
│ └── include/ kernel-internal headers
├── taskman/ The Task Manager (QNX's "procnto"), a user-mode server
│ ├── sys/ system manager: main, ELF loader, support
│ ├── proc/ process manager: spawn, wait, …
│ ├── mem/ memory manager: page tables, physical allocator
│ │ └── pageman/ page-granularity virtual-memory operations
│ └── path/ path manager: namespace, /dev/*, /proc/*
├── lib/ C library and runtime
├── include/ User-space-visible headers (the public ABI)
├── userland/ Shell, utilities, drivers, servers (resource managers)
├── servers/ pci, slogger
├── dev/ Device drivers (virtio block, 8250 UART, …)
├── boot/ Boot artifacts and deploy helpers
├── host_tools/ Host-side tooling (mkgpt.py, …)
├── doc/ Documentation, incl. The QRV Porting Story (LaTeX)
├── Kconfig, Makefile, def.mk, common.mk, emu.sh
Desde ahí, cd os && make construye el sistema (ver §9).
QRV es un sistema de microkernel real. La pila de privilegios, desde el metal hacia arriba, se ve así en RISC-V:``` ┌───────────────────────────────────────────────────────────────────────┐ │ User space (U-mode) │ │ │ │ sh pidin lspci sloginfo login ... │ │ │ │ │ │ │ │ │ devb-nvme fs-qrv devc-ser* pci slogger ← resource managers │ │ │ │ │ │ │ │ │ │ └──────┴───────┴────────┴─────────┴───────┘ │ │ │ libc (send / receive / reply stubs) │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ taskman — process · memory · path manager │ │ │ │ a U-mode server, privileged via TM_PRIV │ │ │ └─────────────────────────────────────────────────────────────────┘ │ └────────────────────────────────────│──────────────────────────────────┘ │ ecall (syscall / message) ┌───────────────────────────────────────────────────────────────────────┐ │ QRV microkernel (S-mode) │ │ │ │ message passing · channels & connections · scheduling · │ │ threads · synchronization · signals · timers & clocks · │ │ interrupts · syscall dispatch · TM_PRIV kernel extensions │ └───────────────────────────────────────│───────────────────────────────┘ │ SBI ecall ┌───────────────────────────────────────────────────────────────────────┐ │ OpenSBI firmware (M-mode) │ └───────────────────────────────────────│───────────────────────────────┘ │ ┌───────────────────────────────────────────────────────────────────────┐ │ RISC-V 64-bit hardware — QEMU virt · SiFive Unmatched U740 │ └───────────────────────────────────────────────────────────────────────┘
**El kernel (modo S)** es el único componente que se ejecuta con privilegios en el sentido clásico. Sus subsistemas son pequeños y enfocados:
- **Paso de mensajes** — `ker_message.c`, `ker_fastmsg.c`, `nano_message.c`
- **Canales y conexiones** — `ker_channel.c`, `ker_connect.c`
- **Hilos y planificación** — `ker_thread.c`, `ker_sched.c`, `nano_sched.c`
- **Sincronización** — `ker_sync.c`, `nano_sync.c`
- **Señales** — `ker_signal.c`
- **Temporizadores y relojes** — `ker_timer.c`, `ker_clock.c`
- **Interrupciones** — `ker_interrupt.c`
- **Despacho de llamadas al sistema** — `ker_call_table.c`
- **Transferencia de datos** — la familia `nano_xfer*.c` (el motor de copia entre espacios de direcciones que mueve cargas de mensajes de forma segura entre procesos)
La interfaz entre el cargador de arranque y el kernel es la **syspage** (`include/sys/syspage.h`); el estado por CPU reside en la **cpupage**; el contexto completo de registros es `RISCV_CPU_REGISTERS`. En RISC-V, cada "CPU" se identifica por su **hart ID** en todas partes — hay un único espacio de nombres para nombrar CPUs, de extremo a extremo.
**Todo lo demás es un proceso de usuario.** El administrador de procesos/memoria/ruta, los controladores de bloque y serie, el sistema de archivos, el servidor PCI, el registrador del sistema — todos son administradores de recursos a los que se accede enviando un mensaje. El kernel no contiene un sistema de archivos; contiene la capacidad para que un proceso le pida a otro que *sea* un sistema de archivos.
---
## 5. Taskman y la llamada privilegiada `TM_PRIV`
**`taskman`** es el nombre de QRV para lo que QNX llamaba `procnto`: el combinado **administrador de procesos, administrador de memoria y administrador de rutas (espacio de nombres)**. En un sistema QNX clásico, este código está fusionado en la imagen del kernel. Uno de los logros estructurales más importantes de QRV es que **taskman ahora se ejecuta en modo usuario** — es un proceso de modo U ordinario, no parte del kernel privilegiado.
Eso plantea una pregunta obvia: si taskman vive en el espacio de usuario, ¿cómo hace las cosas profundamente privilegiadas que un administrador de procesos y memoria debe hacer — manipular tablas de páginas, asignar RAM física, crear y destruir espacios de direcciones, entregar señales y pulsos, eliminar procesos?
La respuesta es una única puerta de enlace estrictamente controlada: **`__KER_TM_PRIV`**, ranura de llamada al sistema 2. Es la *única* puerta a través de la cual taskman llega a operaciones privilegiadas del kernel, y detrás de esa única ranura hay una tabla de despacho de **~98 suboperaciones** — pequeñas **extensiones del kernel** ("kerexts") que cada una realiza una acción privilegiada bien definida y retorna. Algunas familias representativas:
- **Ciclo de vida del proceso** — `PROCESS_CREATE`, `PROCESS_EXEC`,
`PROCESS_DESTROY`, `PROCESS_STARTUP`, `PROCESS_SHUTDOWN`, `REPARENT`
- **Memoria física y virtual** — `PA_ALLOC`, `PA_FREE`,
`PA_QUANTUM_TO_PADDR`, `PAGE_CONT`, `STACK_CONT`, `ASPACE_MEMCPY`
- **Credenciales y límites** — `CRED_GET`, `CRED_SET`, `LIMITS_GET/SET`
- **Entrega y objetos** — `PULSE_DELIVER`, `SIGNAL_DELIVER`,
`QUERY_OBJECT`, `CHANNEL_DESTROY`, `CONNECT_DETACH`
- **SMP y plataforma** — `SMP_BRINGUP`, `LEGAL_CPU_MASK`, `GET_KERN_PGDIR`,
`ICACHE_SYNC` (la operación de coherencia de caché de instrucciones RISC-V nueva en v0.43)
Este diseño mantiene la **base de computación de confianza pequeña** — el kernel real permanece mínimo — mientras le da a taskman exactamente las primitivas privilegiadas que necesita y **nada más**. Fundamentalmente, el montón del kernel y otros internos del kernel permanecen inaccesibles para el modo U (las páginas del kernel mantienen `PTE_U=0`); taskman realiza su trabajo a través de kerexts de copia desde/hacia usuario, nunca recibiendo un puntero crudo del kernel. El enum está en `kernel/include/ker+tm/tm_kercalls.h`; la tabla de despacho está en `kernel/ker_tm_priv.c`.
---
## 6. El Gran Bloqueo del Kernel — y su eliminación
El QRV temprano — como la generación QNX de la que provenía en sistemas multiprocesador — protegía el kernel con un único **Gran Bloqueo del Kernel (BKL)**: una palabra global `inkernel` que admitía exactamente un hart en el kernel a la vez. Sin importar cuántas CPUs estuvieran ejecutándose, cada llamada al sistema y cada mensaje de taskman se serializaban a través de ese único bloqueo. Correcto, simple — y un techo duro para la escalabilidad SMP.
**A partir de v0.42, el BKL ha desaparecido.** Este fue el titular de una larga línea de candidatos a lanzamiento y el tema de los capítulos 9–14 de *The QRV Porting Story*. Las llamadas al sistema y los mensajes de taskman ahora se ejecutan **concurrentemente entre harts** bajo bloqueos de grano fino por objeto:
- **Bloqueo por objeto.** Un bloqueo por `tChannel` y por `tConnect` protege las colas de mensajes; un `vec_slock` por proceso protege el vector de hilos; un `sched_slock` por despacho protege las colas de ejecución; un `alloc_slock` protege el montón del kernel.
- **Paso de mensajes sin bloqueo.** `MsgSend` / `MsgReceive` / `MsgReply` y la familia `Sync*` **no toman ningún bloqueo global en absoluto**. El rendezvous entre harts se coordina mediante bits de puente por hilo y barreras de memoria de hardware en lugar de exclusión mutua.
- **Reclamación SMR (equivalente a RCU).** Los hilos, conexiones y canales se retiran mediante reclamación segura de memoria para que las búsquedas sin bloqueo nunca desreferencien un objeto liberado.
En QEMU `virt` con `-smp 8`, el kernel arranca de manera confiable hasta un prompt `login:` y sostiene un bucle de estrés de 300 iteraciones de `pidin` sin bloqueos.
---
## 7. Almacenamiento: `devb-nvme` y `fs-qrv`
Fiel al modelo de micronúcleo, el almacenamiento en QRV consiste en **dos procesos de usuario cooperantes**, no en un subsistema del kernel:
- **`devb-nvme`** — el controlador de dispositivo de bloque. Habla NVMe sobre PCIe (con análisis de particiones GPT incorporado), descubre el controlador a través del servidor PCI y presenta dispositivos de bloque como `/dev/nvme0n1` y sus particiones. (Un gemelo `devb-virtio` maneja el dispositivo virtio-blk de QEMU para el objetivo emulado.)
- **`fs-qrv`** — el administrador de recursos del sistema de archivos. Monta una partición y sirve el espacio de nombres del sistema de archivos POSIX mediante paso de mensajes: las operaciones `open`/`read`/`write`/`close` de una aplicación se convierten en mensajes que `fs-qrv` responde.
Un arranque típico monta una partición NVMe real y ejecuta programas desde ella:```
mount -t qrv /dev/nvme0n1p5 /disk2
Este camino — el controlador de bloques, la partición, el servidor del sistema de archivos y el kernel intermediando cada mensaje entre ellos — se ejecuta de principio a fin tanto en QEMU como en la unidad NVMe de la SiFive Unmatched.
QRV arranca a través de sysinit/init, levanta el controlador de la consola serie (devc-ser8250 en QEMU, devc-sersifive en la FU740), el servidor PCI, la pila de almacenamiento, y getty/login, y te lleva a un prompt de shell. Los programas destacados de espacio de usuario:
sh — mksh, el shell Korn de MirBSD. Un shell POSIX real y programable es el shell del sistema; los scripts de arranque (level1.sh, …) son scripts de shell ordinarios.pidin — la herramienta clásica de "información de procesos" de QNX: lista procesos, hilos, sus estados, memoria y más. Es la principal sonda de "¿está el sistema vivo y bien?" de QRV y su carga de trabajo estándar de prueba de estrés.lspci — enumera el bus PCI/PCIe a través del servidor PCI.sloginfo — vuelca el registro del sistema recopilado por el servidor slogger.Junto a estos están los componentes básicos de un sistema multiusuario utilizable: getty y login (con soporte de credenciales/autenticación), mount, shutdown, pipe y utilidades básicas (ls, cat, …). Cada programa QRV es multihilo — como mínimo un hilo principal y un hilo del sistema — que es exactamente por qué la sincronización SMP correcta (ver §6) es tan importante.
Cross-compiler : riscv64-linux-gnu-gcc CPU flags : -march=rv64g -mcmodel=medany -mno-relax Build style : -nostdinc -nostdlib -ffreestanding
### Comandos comunes (ejecutar dentro de `os/`)```bash
make # Build everything: startup + kernel + module package
make -Bj # Force a full parallel rebuild (do this after header changes)
make startup # Build startup only
make kernel # Build kernel only
make modpkg # Create the module package (CPIO)
make qemu # Build and run in QEMU
./emu.sh # Run in QEMU (4 CPUs, 256M RAM, virt machine)
./emu.sh -P 1 # Run with a single hart
./emu.sh -gdb # Run with the GDB remote stub (port 1234)
La plataforma de prueba principal es qemu-system-riscv64 en la máquina virt. La configuración se basa en Kconfig.
El objetivo secundario pero serio de QRV es la SiFive Unmatched (FU740) — una placa real de estación de trabajo RISC-V. Lograr que un microkernel que arranca limpiamente en QEMU también arranque limpiamente en silicio físico sacó a la luz una clase de errores que la emulación simplemente no muestra, y perseguirlos es gran parte de lo que tratan los lanzamientos recientes.
El ejemplo definitorio, corregido en v0.43: durante dos meses QRV funcionó impecablemente en QEMU y fallaba en hardware real. En la FU740, cualquier programa — pidin, lspci, cualquier cosa — se estrellaba después de unos pocos spawns, cada fallo diferente del anterior, el contador de programa vagando hacia basura. La causa resultó no ser corrupción de memoria en absoluto, sino incoherencia de caché de instrucciones: RISC-V no garantiza coherencia entre los almacenes de datos y la búsqueda de instrucciones, por lo que el código de programa recién cargado es invisible para la unidad de búsqueda de un hart hasta que ese hart ejecuta fence.i — y el código que se ejecutará en un hart diferente del que lo cargó necesita un fence.i remoto allí. El cargador de QRV no hizo ni lo uno ni lo otro (incluso calculó una bandera "invalidar I-cache" y luego la desechó). QEMU no modela ninguna caché de instrucciones, por lo que el error era invisible allí y determinista en la U74.
La corrección — un fence.i local más una transmisión SBI (cpu_icache_sync_all()) en cada punto donde una página se vuelve ejecutable — convirtió un bucle de spawn que fallaba cada pocas ejecuciones en uno que funcionó más de 600 spawns consecutivos limpios en la FU740. La investigación completa, incluyendo la hipótesis incorrecta que primero produjo y el diagnóstico que la refutó, es la sección final del capítulo 14 de The QRV Porting Story.
Los hitos de hardware alcanzados hasta ahora incluyen arrancar hasta un indicador login: en taskman en modo de usuario en la FU740, y montar y ejecutar programas de prueba desde una partición NVMe real.
QNX es uno de los diseños de micronúcleo más influyentes jamás enviados. Su modelo de enviar/recibir/responder ha enseñado a generaciones de ingenieros de sistemas cómo puede ser una arquitectura de SO limpia. Y sin embargo, el código que lo encarna ha pasado más de una década en un limbo peculiar: lo suficientemente visible para estudiar bajo una licencia comunitaria, pero no lo suficientemente libre para redistribuir, evolucionar o construir un ecosistema vivo a su alrededor. Un diseño tan bueno merece algo mejor que ser preservado solo como un artefacto de solo lectura.
Esa es la razón por la que existe este trabajo. QRV es, francamente, un vehículo transitorio — una forma de aprender la arquitectura profundamente portándola, desmontándola y volviéndola a armar en nuevo hardware, eliminando el bloqueo del núcleo y elevando el administrador de procesos al espacio de usuario y descubriendo exactamente qué suposiciones eran portantes. Cada error perseguido en silicio real, cada subsistema reescrito para ser limpio en 64 bits, cada límite propietario desmantelado, es conocimiento que una implementación verdaderamente libre necesitará.
Porque el objetivo a largo plazo no es mantener una copia parcheada de las fuentes de otro para siempre. Es un sistema operativo completamente libre, desde cero, que es compatible con las interfaces de QNX y fiel a su filosofía de micronúcleo, pero que no debe nada al código propietario — uno que pueda ser usado, enseñado, distribuido y mejorado sin pedir permiso a nadie. QRV es cómo demostramos que tal sistema no solo es posible sino práctico, y cómo ganamos la experiencia para construirlo correctamente.
Si desea que los fundamentos históricos mismos se vuelvan libres, agregue su nombre a PETITION.md. Y si desea ver cómo se ve un micronúcleo moderno y honestamente diseñado desde adentro — clone el recipe, ejecute obtain_proj.sh y lea el código.
QRV — adaptación y reimplementación de QNX Neutrino 6.4 para hardware de 64 bits. Iniciado la víspera de Navidad de 2020. Apache 2.0 (código propio de QRV) + BlackBerry QCL 2.0 (fuentes derivadas de QNX). Vea WHAT_IS_WHAT.md para el desglose componente por componente.
| Archivo / dir | Propósito |
|---|
obtain_proj.sh | El script de reconstrucción — ejecútelo. |
placement.txt | Mapa de cada ruta ascendente de QNX a su ruta en QRV (≈680 entradas). |
patches/ | La serie de parches de QRV, comprimidos con LZ4, más un archivo de orden series. |
LICENSE.txt | Licencia Apache 2.0. |
WHAT_IS_WHAT.md | Desglose componente por componente de licencias y procedencia. |
PETITION.md | La petición de relicenciamiento. |