Sunday, June 15, 2008

OS hello world.

Je, hace rato que no escribía nada, vamos a ver si sale un hello world de un """sistema operativo""", en VirtualBox.

Primero organizamos el dispositivo de arranque, que seria una imagen de un disco. Bueno tenemos que entender bien lo de MBR, eso de cilindros, sectores y cabezas.

Sector: Agrupación lógica de 512 Bytes en el disco. La medida que muestran programas como fdisk es Sector/Track, o sea cantidad de sectores en una pista del disco.

Track: Es una "circunferencia de datos", en cada superficie magnetica. Podemos calcular la cantidad de bytes en un track con sector/track*512.

Head: Representa la cantidad de dispositivos lectores magnéticos, seria como la cantidad de superficies magnéticas disponibles en el disco, aunque la medida que entregan programas como fdisk no representa esto, puede tomarse como una especie de medida para poder ubicar un byte en el disco.

Cylinder: Estas es la cantidad total de tracks que existen en una sola superficie magnética.

Entonces podemos decir que la unidad de almacenamiento es 1 Bit, un byte lo componen 8 Bits, un sector lo componen 512 Bytes, una pista(track) la componen (sector/track) tracks o (sector/track)*512 Bytes, una superficie magnetica esta compuesta por (cylinders) tracks, o (cylinders)*(sector/track)*512 Bytes, y el disco completo esta compuesto por (heads) superficies magneticas o (heads)*(cylinders)*(sectors/track)*512 Bytes. (http://www.storagereview.com/guide2000/ref/hdd/index.html)

MBR: Master Boot Record, la misma esta ubicada en el primer sector del disco (primeros 512 Bytes).

Ahora necesitamos generar una imagen con mbr, si queremos un disco con 16 heads, 63 sectors/track y 160 cylinders:


$ dd if=/dev/zero of=disk.image bs=512 count 161280


Ahora con losetup montamos este archivo como un dispositivo de bloques:


$ sudo losetup /dev/loop0 disk.image


Creamos el MBR con:


$ sudo fdisk -u -C160 -S63 -H16 /dev/loop0


Con -C decimos los cilindros, con -S los sectores por pista, con -H las cabezas, y con -u decimos que nos muestre los tamaños en sectores y no en cilindros. Ahora creamos una particion primaria de todo el disco con el flag de boot activo, la tabla de particiones debe quedar:


Command (m for help): p

Disk /dev/loop0: 82 MB, 82575360 bytes
16 heads, 63 sectors/track, 160 cylinders, total 161280 sectors
Units = sectors of 1 * 512 = 512 bytes
Disk identifier: 0x19c7acc5

Device Boot Start End Blocks Id System
/dev/loop0p1 * 63 161279 80608+ 83 Linux



En este punto tenemos una imagen de un disco solo con MBR, en http://en.wikipedia.org/wiki/Mbr podemos ver una descripcion de las cabeceras del MBR, y podemos verificar en nuestra imagen con:


$ hd -v -n512 disk.image
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001b0 00 00 00 00 00 00 00 00 c5 ac c7 19 00 00 00 01 |................|
000001c0 01 00 83 0f 3f 9f 3f 00 00 00 c1 75 02 00 00 00 |....?.?....u....|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200


Esto corresponde al primer sector de nuestro disco, podemos ver la entrada para la particion primaria que hicimos, la cual es:


$ hd -v -s446 -n16 disk.image
000001be 00 01 01 00 83 0f 3f 9f 3f 00 00 00 c1 75 02 00 |......?.?....u..|
000001ce


Aun falta darle formato a la particion que creamos. La anterior cabecera nos dice que esta particion comienza en el sector 0x3F000000, (little-endian -> 63), y tiene una extension de 0xC1750200 (161217) sectores.

A esta particion le damos formato asi:


$ sudo losetup -o32256 /dev/loop1 disk.image
$ sudo mkfs.ext2 -b1024 /dev/loop1 80608


Sabemos que podemos comenzar a hacer uso de nuestra particion desde el sector 63, que equivale al byte 63*512=32256, y tiene una extension de 161217*512 bytes, y como manejamos bloques de 1024 en ext2, tendriamos un total de 80608.5 bloques que redondeamos a 80608.

Ahora tenemos que instalar el grub en nuestra imagen, esta es la parte mas confusa, es buena una cuanta documentacion (http://www.gnu.org/software/grub/ , http://www.gnu.org/software/grub/manual/multiboot/ )

Debemos instalar todo lo necesario en nuestro sistema de archivos en la carpeta /boot/grub.


$ mkdir mnt
$ sudo mount -text2 /dev/loop1 mnt/
$ wget http://ftp.br.debian.org/debian/pool/main/g/grub/grub_0.97-27_i386.deb
$ dpkg-deb -X grub_0.97-27_i386.deb grub_debian
$ sudo mkdir -p mnt/boot/grub
$ sudo cp grub_debian/usr/lib/grub/i386-pc/* mnt/boot/grub/
$ sudo grub

grub> device (hd0) /dev/loop0

grub> geometry (hd0) 160 16 63
drive 0x80: C/H/S = 160/16/63, The number of sectors = 161280, /dev/loop0
Partition num: 0, Filesystem type is ext2fs, partition type 0x83

grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83

grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... failed

Error 22: No such partition

grub> quit


Aqui bajamos el paquete de debian de grub, del cual copiamos todo lo necesario para que grub funcione en una arquitectura i386 que es la que pensamos virtualizar. Encontramos un eror en grub, pero si tiramos el comando con strace vemos lo siguiente(http://osdir.com/ml/boot-loaders.grub.bugs/2005-01/msg00035.html) :


$ sudo strace -o trace.log grub
...
$ cat trace.log | grep loop
open("/dev/loop0", O_RDONLY|O_LARGEFILE) = 4
open("/dev/loop0", O_RDWR|O_LARGEFILE) = 3
open("/dev/loop01", O_RDWR|O_LARGEFILE) = -1 ENOENT (No such file or directory)


Grub cree que la particion 1 de nuestra imagen se encuentra en loop01, entonces ahi se la daremos:


$ sudo ln -s /dev/loop1 /dev/loop01


Volvemos a tirar grub:


$ sudo grub

grub> device (hd0) /dev/loop0

grub> geometry (hd0) 160 16 63
drive 0x80: C/H/S = 160/16/63, The number of sectors = 161280, /dev/loop0
Partition num: 0, Filesystem type is ext2fs, partition type 0x83

grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83

grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded. succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded
Done.

grub> quit



Ya podemos verificar la instalacion del stage1 en el mbr:

$ hd -v -n512 disk.image

00000000 eb 48 90 00 00 00 00 00 00 00 00 00 00 00 00 00 |.H..............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 02 |................|
00000040 ff 00 00 20 01 00 00 00 00 02 fa 90 90 f6 c2 80 |... ............|
00000050 75 02 b2 80 ea 59 7c 00 00 31 c0 8e d8 8e d0 bc |u....Y|..1......|
00000060 00 20 fb a0 40 7c 3c ff 74 02 88 c2 52 be 7f 7d |. ..@|<.t...R..}|
00000070 e8 34 01 f6 c2 80 74 54 b4 41 bb aa 55 cd 13 5a |.4....tT.A..U..Z|
00000080 52 72 49 81 fb 55 aa 75 43 a0 41 7c 84 c0 75 05 |RrI..U.uC.A|..u.|
00000090 83 e1 01 74 37 66 8b 4c 10 be 05 7c c6 44 ff 01 |...t7f.L...|.D..|
000000a0 66 8b 1e 44 7c c7 04 10 00 c7 44 02 01 00 66 89 |f..D|.....D...f.|
000000b0 5c 08 c7 44 06 00 70 66 31 c0 89 44 04 66 89 44 |\..D..pf1..D.f.D|
000000c0 0c b4 42 cd 13 72 05 bb 00 70 eb 7d b4 08 cd 13 |..B..r...p.}....|
000000d0 73 0a f6 c2 80 0f 84 ea 00 e9 8d 00 be 05 7c c6 |s.............|.|
000000e0 44 ff 00 66 31 c0 88 f0 40 66 89 44 04 31 d2 88 |D..f1...@f.D.1..|
000000f0 ca c1 e2 02 88 e8 88 f4 40 89 44 08 31 c0 88 d0 |........@.D.1...|
00000100 c0 e8 02 66 89 04 66 a1 44 7c 66 31 d2 66 f7 34 |...f..f.D|f1.f.4|
00000110 88 54 0a 66 31 d2 66 f7 74 04 88 54 0b 89 44 0c |.T.f1.f.t..T..D.|
00000120 3b 44 08 7d 3c 8a 54 0d c0 e2 06 8a 4c 0a fe c1 |;D.}<.T.....L...|
00000130 08 d1 8a 6c 0c 5a 8a 74 0b bb 00 70 8e c3 31 db |...l.Z.t...p..1.|
00000140 b8 01 02 cd 13 72 2a 8c c3 8e 06 48 7c 60 1e b9 |.....r*....H|`..|
00000150 00 01 8e db 31 f6 31 ff fc f3 a5 1f 61 ff 26 42 |....1.1.....a.&B|
00000160 7c be 85 7d e8 40 00 eb 0e be 8a 7d e8 38 00 eb ||..}.@.....}.8..|
00000170 06 be 94 7d e8 30 00 be 99 7d e8 2a 00 eb fe 47 |...}.0...}.*...G|
00000180 52 55 42 20 00 47 65 6f 6d 00 48 61 72 64 20 44 |RUB .Geom.Hard D|
00000190 69 73 6b 00 52 65 61 64 00 20 45 72 72 6f 72 00 |isk.Read. Error.|
000001a0 bb 01 00 b4 0e cd 10 ac 3c 00 75 f4 c3 00 00 00 |........<.u.....|
000001b0 00 00 00 00 00 00 00 00 cc 69 95 5f 00 00 80 01 |.........i._....|
000001c0 01 00 83 0f 3f 9f 3f 00 00 00 c1 75 02 00 00 00 |....?.?....u....|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200



Comencemos con el codigo, primero necesitamos la porcion de codigo que nos salta al codigo en C que queremos ejecutar, esta primera parte viene en assembly, aqui hay uno que nos puede servir (tomado de http://javiervalcarce.es/wiki/Kernel_multiboot_m%C3%ADnimo):



/* Start-up */

#define STACK_SIZE 0x4000 /* The size of our stack (16KB) */
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 /* The magic number for the Multiboot header */

.text
.globl _start

.align 4 /* Align 32 bits boundary */
/* Multiboot-compliant header */
.long +MULTIBOOT_HEADER_MAGIC /* magic */
.long 0 /* flags=0 */
.long -MULTIBOOT_HEADER_MAGIC /* checksum */
_start:
movl $(stack + STACK_SIZE), %esp /* Initialize the stack pointer */

push $0 /* Reset EFLAGS */
popf

call cmain /* C main function... */
loop: hlt
jmp loop

.comm stack, STACK_SIZE /* Our stack area */



lo guardamos en un archivo llamado startup.S, ahora el codigo en C:

/* kernel.c - the C part of the kernel */

/* Some screen stuff. */
#define COLUMNS 80 /* The number of columns */
#define LINES 24 /* The number of lines */
#define ATTRIBUTE 7 /* The attribute of an character */
#define VIDEO 0xB8000 /* The video memory address */

/* Variables */
static int xpos; /* Save the X position */
static int ypos; /* Save the Y position */
static volatile
unsigned char *video; /* Point to the video memory */

/* Forward declarations */
void cmain (unsigned long magic, unsigned long addr);
static void cls();
static void putchar (int c);


////////////////////////////////////////////////////////////////////////////////
void cmain (unsigned long magic, unsigned long addr)
{

char msg[] = "Hello World";
char* p;

cls();

/* printf */
p = msg;
for (p = msg; *p != 0; p++)
{
putchar(*p);
}
}

////////////////////////////////////////////////////////////////////////////////
/* Clear the screen and initialize VIDEO, XPOS and YPOS */
static void cls (void)
{
int i;

video = (unsigned char *) VIDEO;

for (i = 0; i < xpos =" 0;" ypos =" 0;" c ="=" c ="=" xpos =" 0;">= LINES)
ypos = 0;
return;
}

*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;

xpos++;
if (xpos >= COLUMNS)
goto newline;
}


Guardamos esto en un kernel1.c, y compilamos :


$ gcc -c kernel1.c
$ gcc -c startup.S
$ ld -o kernel1 kernel1.o startup.o -Ttext 0x100000


Ahora terminamos de configurar el grub para que funcione con nuestro kernel1:


$ sudo cp kernel1 mnt/boot/


Creamos el archivo menu.lst con:


default 0
timeout 2
color cyan/blue white/blue

title kernel1
root (hd0,0)
kernel /boot/kernel1


Y lo copiamos a boot/grub/menu.lst:


$ sudo cp menu.lst mnt/boot/grub/


Ya deberiamos tener nuestro ambiente listo, falta convertir esta imagen en un disco que virtualbox pueda leer:


$ sudo umount mnt
$ sudo losetup -d /dev/loop1
$ sudo losetup -d /dev/loop0
$ sudo rm /dev/loop01
$ vditool DD PruebaOS.vdi disk.image


Prendemos nuestro VirtualBox, organizamos una maquina virtual cuyo disco sea el que acabamos de crear, mmmm, bueno, grub arranco, sale la lista con kernel1, intenta correrlo, pero sale un error "Error 13: Invalid or unsupported executable format" , aaaa, buen detalle:


$ uname -m
x86_64


De seguro si tienen una maquina i386 les funciona de una.
Listo, entonces compilo en chroot de i386:


$ cp kernel1.c startup.S /var/etch-i386-20080604/tmp/
$ sudo chroot /var/etch-i386-20080604/ /bin/bash
# cd /tmp
# gcc -c kernel1.c
# gcc -c startup.S
# ld -o kernel1 kernel1.o startup.o -Ttext 0x100000
# exit
$ sudo losetup -o32256 /dev/loop1 disk.image
$ sudo mount -text2 /dev/loop1 mnt/
$ sudo cp /var/etch-i386-20080604/tmp/kernel1 mnt/boot/kernel1
$ sudo umount mnt
$ sudo losetup -d /dev/loop1
$ rm PruebaOS.vdi
$ vditool DD PruebaOS.vdi disk.image


Otra vez abrimos virtualbox, borramos la configuracion con el disco anterior, ponemos el nuevo y corremos la maquina.

Listo eso es todo, de ahí a hacer un kernelcito decente hay un paso, jaja.

1 comment:

Juan Alvarez said...

George, quedo excelente el tutorial!!