Вы находитесь на странице: 1из 8

GRUB2 EFI Support

GRUB2 EFI Support


Finnbarr P. Murphy
(fpm@fpmurphy.com)

Most GNU/Linux distributions utilize GNU GRUB (GRand Unified Bootloader) to boot the Linux
kernel. GRUB was conceived and initially developed by Erich Boleyn. It became part of the GNU
project in 1999 but was never officially released. The last development snapshot was v0.97 which
was released in 2005. In spite of the fact that it was never officially released, it is used on millions
of computers worldwide.

ly
The v0.97 codebase is now known as GRUB Legacy. Supposedly it is still in maintenance mode but
no fixes have occurred in the last few years. As a result, vendors such as Red Hat maintain their

on
own branches of GRUB Legacy. Currently the Fedora Project applies 25 patches to the v0.97
release of which 7 or 8 patches relate to the addition of EFI support. Other forks of GRUB Legacy
include the bootloader used by Oracle to boot Solaris on IA32 and X64 platforms.

se
Many years ago the GRUB developers decided that a complete rewrite was required. The goal was
to make a more robust, portable and powerful version of GRUB which has support for a scripting
language, internationalization and localization. GRUB 2 is derived from PUPA which was a
lu
research project funded by the Information-technology Promotion Agency of Japan to investigate
the next generation of GRUB.
a
For some time, I have been EFI-booting one of my X64 systems which runs Fedora 12 and
experimenting with the TianoCore EFI shell and various EFI utilities. The system boots into an EFI
nn

shell and I then use Fedora’s EFI-enabled version of GRUB Legacy to boot into various Fedora
kernels.
o

Recently I decided to download the latest version of the GRUB 2 sources (currently v1.98), build
GRUB2 as an X64 EFI image and install it in parallel with the GRUB Legacy bootloader on the ESP
rs

(EFI System Partition) so that I could experiment with the new facilities available in the GRUB2
bootloader.
pe

Building the EFI version of GRUB2 was not an issue. As usual, the first requirement is to configure
the build. Here is a copy of the configuration options I choose:
r
Fo

$ ./configure --with-platform=efi --enable-grub-fstest=no


--enable-grub-mkfont=no --disable-nls --enable-efiemu=no

As you can see I disabled EFI emulation, NLS support and various debugging utilities (fstest and
mkfont.) The Makefile includes ../conf/x86_64-efi.mk which is where most of the build is specified.
The default Makefile has a dependency on Ruby but this is easily worked around. The header
../include/grub/i18n also has a minor error. The fix is to change the syntax of the ENABLE_NLS
guard from #if to #ifdef.

After you have successfully run make, you have to generate the grub.efi image using the
grub-mkimage utility.

$ ./grub-mkimage -d . -o grub.efi boot linux part_gpt \


fat ext2 normal sh configfile lspci ls reboot datetime
loadenv search lvm help quit

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 1/8


GRUB2 EFI Support

GRUB2 is a very modular bootloader. You can include whichever modules you need into the image
using grub-mkimage or you can load them from the GRUB2 shell. The boot module is mandatory,
the linux module is the loader for Linux images, the help module displays help information, and
the remaining modules, except for quit are explained in Erich Boleyn’s commands summary page.

Here is a list of the modules which were built:

acpi.mod efi_gop.mod gcry_whirlpool.mod mmap.mod scsi.mod


affs.mod efi_uga.mod gettext.mod msdospart.mod search_fs_
file.mod
afs_be.mod elf.mod gfxmenu.mod multiboot2.mod search_fs_
uuid.mod

ly
afs.mod ext2.mod gfxterm.mod multiboot.mod search_lab
el.mod
appleldr.mod extcmd.mod gptsync.mod normal.mod search.mod

on
ata.mod fat.mod gzio.mod ntfscomp.mod setjmp.mod
at_keyboard.mod fixvideo.mod halt.mod ntfs.mod setpci.mod
befs_be.mod font.mod handler.mod part_acorn.mod sfs.mod
befs.mod fshelp.mod hashsum.mod part_amiga.mod sh.mod
bitmap.mod functional_test.mod help.mod part_apple.mod sleep.mod

se
bitmap_scale.mod gcry_arcfour.mod hexdump.mod part_gpt.mod tar.mod
blocklist.mod gcry_blowfish.mod hfs.mod part_msdos.mod terminal.m
od
boot.mod gcry_camellia.mod
lu
hfsplus.mod part_sun.mod terminfo.m
od
bufio.mod gcry_cast5.mod iso9660.mod parttool.mod test.mod
cat.mod gcry_crc.mod jfs.mod password.mod tga.mod
a
chain.mod gcry_des.mod jpeg.mod password_pbkdf2.mod trig.mod
charset.mod gcry_md4.mod keystatus.mod pbkdf2.mod true.mod
nn

cmp.mod gcry_md5.mod linux.mod pci.mod udf.mod


configfile.mod gcry_rfc2268.mod loadbios.mod png.mod ufs1.mod
cpio.mod gcry_rijndael.mod loadenv.mod probe.mod ufs2.mod
cpuid.mod gcry_rmd160.mod loopback.mod quit.mod vga_text.m
o

od
crc.mod gcry_seed.mod ls.mod raid5rec.mod video_fb.m
rs

od
crypto.mod gcry_serpent.mod lspci.mod raid6rec.mod video.mod
datehook.mod gcry_sha1.mod lvm.mod raid.mod videotest.
pe

mod
date.mod gcry_sha256.mod mdraid.mod read.mod xfs.mod
datetime.mod gcry_sha512.mod memrw.mod reboot.mod xnu.mod
dm_nv.mod gcry_tiger.mod minicmd.mod reiserfs.mod xnu_uuid.m
od
r

echo.mod gcry_twofish.mod minix.mod relocator.mod


Fo

After building the grub.efi image I moved it to its home on the ESP and created a very basic
GRUB2 configuration file which must be called grub.cfg. Here is a copy of my initial grub.cfg

# FPM 3/18/10 manual edit


timeout=10
default=0
set color_normal=yellow/blue
set color_highlight=blue/yellow
menuentry "Fedora 12" {
root=(hd0,2)
linux /vmlinuz-2.6.32.9-70.fc12.x86_64 ro root=/dev/mapper/vg_ultra-lv_root LANG=en_US.
UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us rhgb quiet nolapic
initrd /initramfs-2.6.32.9-70.fc12.x86_64.img
}

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 2/8


GRUB2 EFI Support

Stanzas are phrased slightly differently and cannot be copied directly from a GRUB Legacy
configuration file. They are pretty close though and no explanation of the required changes should
be necessary. Note that the command kernel has been superseded by linux. Note also that GRUB2
partition numbers start at 1 not 0 but drives still start at 0. Thus the second partition of the first
drive is hd(0,2) in GRUB2 but (hd0,1) in GRUB Legacy.

When I switched to GRUB2 to experiment using it to EFI-boot Fedora 12, I immediately noticed
that the time from when I hit the Enter key until I saw a Plymouth theme went from about 10
second to over 3 minutes. It took me a while to track down the cause of this delay as the GRUB2
codebase is significantly larger and more complex than the GRUB Legacy codebase and
EFI-related code is in numerous subdirectories.

After a number of false starts, I finally tracked the issue down to the size of the disk cache. See

ly
the caching algorithm in get_disk_read() in the source file ../kern/disk.c. I probably could have
bypassed the disk cache altogether, but found that increasing the cache size from 8 sectors to

on
8192 sectors reduced the time to read the kernel and initrd image files to less than a few seconds.
Here are the changes to make in ../include/grub/disk.h.

se
/* The size of a disk cache in sector units. */
#ifdef GRUB_MACHINE_EFI
#define GRUB_DISK_CACHE_SIZE 8192
#define GRUB_DISK_CACHE_BITS 13
lu
#else
#define GRUB_DISK_CACHE_SIZE 8
#define GRUB_DISK_CACHE_BITS 3
a
#endif
nn

Note that I used the guard GRUB_MACHINE_EFI to guard any EFI-specific code in common
source files.
o

One useful feature of GRUB2 are the reboot and halt modules. You can use these modules to add
rs

menu options to either reboot or halt your system or you invoke these commands from the GRUB2
command line. For example. here is a reboot menu option.
pe

menuentry "Reboot" {
reboot
}
r
Fo

Currently, no command is available to quit out of GRUB2 and go back to the EFI shell. For non-EFI
platforms this is not a useful option but for EFI-based platforms it provides great flexibility. I
therefore decided to develop such a command and used the source code for the reboot command
as my template. In case anybody else wants to create such a GRUB2 command, here are the
relevant details.

Create a file named quit.c in the ../commands subdirectory of your build tree which contains the
following source code:

/* quit.c - command to return to EFI shell */


/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 3/8


GRUB2 EFI Support

* the Free Software Foundation, either version 3 of the License, or


* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/dl.h>
#include <grub/command.h>
#include <grub/misc.h>
#include <grub/i18n.h>
#include <grub/term.h>

ly
#include <grub/efi/efi.h>
#include <grub/efi/api.h>
#include <grub/efi/console.h>

on
static grub_err_t
grub_cmd_quit (grub_command_t cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char **args __attribute__ ((unused)))
{

se
grub_efi_simple_text_output_interface_t *o;
static grub_uint8_t efi_console_stdcolor =
GRUB_EFI_TEXT_ATTR (GRUB_EFI_YELLOW,
GRUB_EFI_BACKGROUND_BLACK);
o = grub_efi_system_table->con_out;
lu
efi_call_2 (o->set_attributes, o, efi_console_stdcolor);
efi_call_1 (o->clear_screen, o);
grub_exit ();
a
return 0;
}
nn

static grub_command_t cmd;


GRUB_MOD_INIT(quit)
{
cmd = grub_register_command ("quit", grub_cmd_quit,
o

0, N_("Return to EFI shell."));


}
rs

GRUB_MOD_FINI(quit)
{
grub_unregister_command (cmd);
pe

The contents of this file should be self-explanatory to an experienced programmer. The two efi_call
r

s invoke the appropriate EFI protocol calls to set the screen text-mode foreground and
Fo

background colors and clear the screen.

Next add the following make directives to ../conf/x86_64-efi.mk which is the EFI-specific Makefile:

# For quit.mod.
quit_mod_SOURCES = commands/quit.c
clean-module-quit.mod.1:
rm -f quit.mod mod-quit.o mod-quit.c pre-quit.o quit_mod-commands_quit.o und-quit.
lst
CLEAN_MODULE_TARGETS += clean-module-quit.mod.1
clean-module-quit.mod-symbol.1:
rm -f def-quit.lst
CLEAN_MODULE_TARGETS += clean-module-quit.mod-symbol.1
DEFSYMFILES += def-quit.lst
mostlyclean-module-quit.mod.1:
rm -f quit_mod-commands_quit.d
MOSTLYCLEAN_MODULE_TARGETS += mostlyclean-module-quit.mod.1

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 4/8


GRUB2 EFI Support

UNDSYMFILES += und-quit.lst
ifneq ($(TARGET_APPLE_CC),1)
quit.mod: pre-quit.o mod-quit.o $(TARGET_OBJ2ELF)
-rm -f $@
$(TARGET_CC) $(quit_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ pre-quit.o mod-
quit.o
if test ! -z "$(TARGET_OBJ2ELF)"; then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1)
; fi
$(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _
grub_mod_fini -R .note -R .comment $@
else
quit.mod: pre-quit.o mod-quit.o $(TARGET_OBJ2ELF)
-rm -f $@
-rm -f $@.bin
$(TARGET_CC) $(quit_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@.bin pre-quit.o
mod-quit.o

ly
$(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mo
d_fini:grub_mod_fini -wd1106 -nu -nd $@.bin $@
-rm -f $@.bin

on
endif
pre-quit.o: $(quit_mod_DEPENDENCIES) quit_mod-commands_quit.o
-rm -f $@
$(TARGET_CC) $(quit_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ quit_mod-comman
ds_quit.o

se
mod-quit.o: mod-quit.c
$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(quit_mod_CFLAGS) -c -o $@ $<
mod-quit.c: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh
sh $(srcdir)/genmodsrc.sh 'quit' $< > $@ || (rm -f $@; exit 1)
ifneq ($(TARGET_APPLE_CC),1)
lu
def-quit.lst: pre-quit.o
$(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 quit/' > $@
else
a
def-quit.lst: pre-quit.o
$(NM) -g -P -p $< | grep -E '^[a-zA-Z0-9_]* [TDS]' | sed 's/^\([^ ]*\).*/\1 quit/
nn

' > $@
endif
und-quit.lst: pre-quit.o
echo 'quit' > $@
o

$(NM) -u -P -p $< | cut -f1 -d' ' >> $@


quit_mod-commands_quit.o: commands/quit.c $(commands/quit.c_DEPENDENCIES)
rs

$(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS) $(TARGET_CFLAGS)


$(quit_mod_CFLAGS) -MD -c -o $@ $<
-include quit_mod-commands_quit.d
pe

clean-module-quit_mod-commands_quit-extra.1:
rm -f cmd-quit_mod-commands_quit.lst fs-quit_mod-commands_quit.lst partmap-quit_mo
d-commands_quit.lst handler-quit_mod-commands_quit.lst parttool-quit_mod-commands_quit.lst
video-quit_mod-commands_quit.lst terminal-quit_mod-commands_quit.lst
CLEAN_MODULE_TARGETS += clean-module-quit_mod-commands_quit-extra.1
r

COMMANDFILES += cmd-quit_mod-commands_quit.lst
Fo

FSFILES += fs-quit_mod-commands_quit.lst
PARTTOOLFILES += parttool-quit_mod-commands_quit.lst
PARTMAPFILES += partmap-quit_mod-commands_quit.lst
HANDLERFILES += handler-quit_mod-commands_quit.lst
TERMINALFILES += terminal-quit_mod-commands_quit.lst
VIDEOFILES += video-quit_mod-commands_quit.lst
cmd-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) gencmdlist.
sh
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)
$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh quit > $@ ||
(rm -f $@; exit 1)
fs-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) genfslist.s
h
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)
$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh quit > $@ || (
rm -f $@; exit 1)
parttool-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) genpa
rttoollist.sh
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 5/8


GRUB2 EFI Support

$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/genparttoollist.sh quit > $


@ || (rm -f $@; exit 1)
partmap-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) genpar
tmaplist.sh
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)
$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh quit > $@
|| (rm -f $@; exit 1)
handler-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) genhan
dlerlist.sh
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)
$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/genhandlerlist.sh quit > $@
|| (rm -f $@; exit 1)
terminal-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) gente
rminallist.sh
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)
$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/genterminallist.sh quit > $

ly
@ || (rm -f $@; exit 1)
video-quit_mod-commands_quit.lst: commands/quit.c $(commands/quit.c_DEPENDENCIES) genvideo
list.sh

on
set -e; $(TARGET_CC) -Icommands -I$(srcdir)/commands $(TARGET_CPPFLAGS)
$(TARGET_CFLAGS) $(quit_mod_CFLAGS) -E $< | sh $(srcdir)/genvideolist.sh quit > $@ |
| (rm -f $@; exit 1)
quit_mod_CFLAGS = $(COMMON_CFLAGS)
quit_mod_LDFLAGS = $(COMMON_LDFLAGS)

se
You should now be able to build quit.mod by invoking make quit.mod from the root of your GRUB2
lu
build tree. Then use the grub-mkimage utility to incorporate the quit module into your grub.efi
image. After placing the newly created grub.efi image in the ESP, you should be able to use the
quit command from the GRUB2 shell or via a stanza in grub.cfg.
a
The current linux loader does not reset the screen text-mode foreground and background colors to
nn

their defaults. For EFI shells these are yellow and black respectively. Instead the GRUB2
foreground and background colors propagate onto text outputted by the loader module. Here are
the diffs to the loader file ../loader/i386/efi/linux.c to correct this problem and add informative
o

messages similar to those outputted by GRUB Legacy.


rs

37a38,41
> #include <grub/efi/console.h>
pe

> #include <grub/i386/vga_common.h>


>
>
240a245,247
> grub_printf("Trying to allocate %u pages for VMLINUZ\n",
r

> (unsigned) prot_mode_pages);


Fo

>
608a616,622
> /* set color colors to EFI default */
> grub_efi_simple_text_output_interface_t *o;
> static grub_uint8_t efi_console_standard_color = GRUB_EFI_TEXT_ATTR (GRUB_EFI_YELLOW,
GRUB_EFI_BACKGROUND_BLACK);
>
> o = grub_efi_system_table->con_out;
> efi_call_2 (o->set_attributes, o, efi_console_standard_color);
>
809c823,824
< grub_printf (" [Linux-bzImage, setup=0x%x, size=0x%x]\n",
---
>
> grub_printf ("[Linux-EFI, setup=0x%x, size=0x%x]\n",

Once I had GRUB2 up and running, I decided to look at how to support a full-screen background
image. This is one of the areas where GRUB Legacy has major limitations. It only supports a

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 6/8


GRUB2 EFI Support

background image with a maximum of 16 colors and a maximum resolution of 640×480.

To test support for a background image in GRUB2, l took a Fedora 12 background image and
converted it into TGA format using GIMP. Why TGA? No particular reason. I could have just as
easily used JPEG or PNG formats. I placed this image in the ESP GRUB2 subdirectory.

Next I needed to obtain a suitable glyph (font) file for displaying characters when in graphics
mode. GRUB2 uses PFF2 bitmap fonts. Rather than going to the trouble of creating my own PFF2
file, I simply extracted the font file unicode.pf2 from the Debian grub-common_1.98 package.

Finally, as shown below, I modified grub.cfg to load and configure the appropriate modules and
display the background image.

ly
#
# FPM 3/19/10

on
#
timeout=20
default=0
if loadfont /efi/grub2/unicode.pf2
then

se
set gfxmode="1024x768x32"
set gfxpayload=keep
insmod gfxterm
insmod efi_gop
terminal_output gfxterm
lu
terminal gfxterm
fi
use_bg=true
a
backgound_image /efi/grub2/fedora.tga
set color_normal=yellow/blue
nn

set color_highlight=blue/yellow
menuentry "Fedora 12" {
root=(hd0,2)
linux /vmlinuz-2.6.32.9-70.fc12.x86_64 ro root=/dev/mapper/vg_ultra-lv_root LANG=en_US.
o

UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us rhgb quiet nolapic


initrd /initramfs-2.6.32.9-70.fc12.x86_64.img
rs

}
menuentry "Reboot" {
reboot
pe

}
menuentry "Exit to EFI Shell" {
quit
}
r
Fo

You will notice that GRUB2 supports a rudimentary scripting language. Here I test that the font
file /efi/grud2/unicode.pf2 is loaded before the gfxterm and efi_gop modules are loaded and
configured. When I rebooted the system and invoked GRUB2 from the EFI shell, the specified
Fedora backgound image covered the whole screen without a border and characters were
correctly displayed.

Is GRUB2 a viable option for EFI-booting Fedora 12 at this time? Yes, provided that it is modified
to minimize the load delay caused by the default small disk caches. Do I like GRUB2? Yes. Actually
I love the flexibility and additional functionality it provides. Broadening the question further, is
GRUB2 ready for primetime use and should Fedora switch to GRUB2 in the near future? The
Ubuntu, Gentoo and Debian distributions of GNU/Linux certainly think so and their latest releases
utilize it as the default bootloader. My answer to both questions is probably no. I personally think
GRUB2 requires some work to harden the product – especially for EFI-booting. Other reasons
include a lack of accurate, up-to-date and complete end-user documentation and an over-complex
product. By over-complex product, I mean the large number of modules and other files. Many of

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 7/8


GRUB2 EFI Support

the modules, for example minix.mod, are of no use to users of Fedora on an IA32 or X64 platform
and should not be distributed as part of a Fedora RPM.

There is a GRUB2 RPM available for Fedora 12. However it contains all the available loadable
modules irrespective of whether they are of any use to Fedora users. Worse still, it installs all the
modules under /usr/lib/grub2/i386-pc/. Why not just put them under /usr/lib/grub2? What value
does the extra i386-pc add? I am on an x86-64 platform; I do not expect to find modules that I
need in a directory called i386-pc. Yes, I understand that the GRUB2 boatloader always uses 32-bit
modules but that fact does not need to be embedded in a subdirectory name. In addition, two
important files are missing from the RPM, i.e. the font files unicode.pf2 and ascii.pf2. Without
these files, background images are not possible.

Should Fedora switch to using GRUB2 in the long term? Certainly. The extra features such as
loadable modules, scripting and localization support are frankly very compelling.

ly
on
se
a lu
o nn
rs
pe
r
Fo

03-10-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 8/8

Вам также может понравиться