From ce5bf700caa436a09f79c7201d8910343c4d403b Mon Sep 17 00:00:00 2001 From: okuji Date: Mon, 20 Jan 2003 04:13:46 +0000 Subject: [PATCH] 2003-01-20 Yoshinori K. Okuji * include/pupa/normal.h: New file. * include/pupa/setjmp.h: Likewise. * include/pupa/i386/setjmp.h: Likewise. * normal/cmdline.c: Likewise. * normal/command.c: Likewise. * normal/main.c: Likewise. * normal/menu.c: Likewise. * normal/i386/setjmp.S: Likewise. * loader/i386/pc/linux.c (pupa_rescue_cmd_linux): Made global. (pupa_rescue_cmd_initrd): Likewise. * loader/i386/pc/chainloader.c (pupa_rescue_cmd_chainloader): Likewise. * kern/i386/pc/startup.S (translation_table): New variable. (translate_keycode): New function. (pupa_console_getkey): Call translate_keycode. * kern/rescue.c (attempt_normal_mode): New function. (pupa_enter_rescue_mode): Attempt to execute the normal mode. If it failed, print a message. * kern/mm.c (pupa_real_malloc): Print more information when a free magic is broken. (pupa_free): If the first free header is not free actually, set it to P. * kern/main.c (pupa_load_normal_mode): Just load the module "normal". (pupa_main): Don't print the message "Entering into rescue mode..." here. * include/pupa/i386/pc/loader.h (pupa_rescue_cmd_initrd): Declared. (pupa_rescue_cmd_initrd): Likewise. (pupa_rescue_cmd_initrd): Likewise. * include/pupa/symbol.h (FUNCTION): Specify the type. (VARIABLE): Likewise. * include/pupa/err.h (pupa_err_t): Added PUPA_ERR_UNKNOWN_COMMAND. * include/pupa/dl.h (pupa_dl_set_prefix): Exported. (pupa_dl_get_prefix): Likewise. * conf/i386-pc.rmk (pkgdata_MODULES): Added normal.mod. Added _chain.mod and _linux.mod instead of chain.mod and linux.mod. (chain_mod_SOURCES): Renamed to ... (_chain_mod_SOURCES): ... this. (chain_mod_CFLAGS): Renamed to ... (_chain_mod_CFLAGS): ... this. (linux_mod_SOURCES): Renamed to ... (_linux_mod_SOURCES): ... this. (linux_mod_CFLAGS): Renamed to ... (_linux_mod_CFLAGS): ... this. (normal_mod_SOURCES): New variable. (normal_mod_CFLAGS): Likewise. (normal_mod_ASFLAGS): Likewise. 2003-01-18 Yoshinori K. Okuji * kern/rescue.c (pupa_rescue_cmd_rmmod): Call pupa_dl_unload, if possible. * kern/dl.c (pupa_dl_ref): Refer dependending modules recursively. (pupa_dl_unref): Unrefer depending modules recursively. Don't call pupa_dl_unload implicitly, because PUPA can crash if a module is unloaded before one depending on that module is unloaded. (pupa_dl_unload): Unload depending modules explicitly, if possible. --- ChangeLog | 78 ++++++++ conf/i386-pc.mk | 163 +++++++++++----- conf/i386-pc.rmk | 20 +- include/grub/dl.h | 4 +- include/grub/err.h | 1 + include/grub/i386/pc/loader.h | 6 + include/grub/i386/setjmp.h | 25 +++ include/grub/normal.h | 131 +++++++++++++ include/grub/setjmp.h | 29 +++ include/grub/symbol.h | 4 +- kern/dl.c | 21 +- kern/i386/pc/startup.S | 47 ++++- kern/main.c | 35 +--- kern/mm.c | 20 +- kern/rescue.c | 23 ++- loader/i386/pc/chainloader.c | 2 +- loader/i386/pc/linux.c | 4 +- normal/cmdline.c | 298 +++++++++++++++++++++++++++++ normal/command.c | 176 +++++++++++++++++ normal/i386/setjmp.S | 57 ++++++ normal/main.c | 351 ++++++++++++++++++++++++++++++++++ normal/menu.c | 272 ++++++++++++++++++++++++++ 22 files changed, 1664 insertions(+), 103 deletions(-) create mode 100644 include/grub/i386/setjmp.h create mode 100644 include/grub/normal.h create mode 100644 include/grub/setjmp.h create mode 100644 normal/cmdline.c create mode 100644 normal/command.c create mode 100644 normal/i386/setjmp.S create mode 100644 normal/main.c create mode 100644 normal/menu.c diff --git a/ChangeLog b/ChangeLog index 6b977b685..7a078f7a0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,81 @@ +2003-01-20 Yoshinori K. Okuji + + * include/pupa/normal.h: New file. + * include/pupa/setjmp.h: Likewise. + * include/pupa/i386/setjmp.h: Likewise. + * normal/cmdline.c: Likewise. + * normal/command.c: Likewise. + * normal/main.c: Likewise. + * normal/menu.c: Likewise. + * normal/i386/setjmp.S: Likewise. + + * loader/i386/pc/linux.c (pupa_rescue_cmd_linux): Made global. + (pupa_rescue_cmd_initrd): Likewise. + + * loader/i386/pc/chainloader.c (pupa_rescue_cmd_chainloader): + Likewise. + + * kern/i386/pc/startup.S (translation_table): New variable. + (translate_keycode): New function. + (pupa_console_getkey): Call translate_keycode. + + * kern/rescue.c (attempt_normal_mode): New function. + (pupa_enter_rescue_mode): Attempt to execute the normal mode. If + it failed, print a message. + + * kern/mm.c (pupa_real_malloc): Print more information when a + free magic is broken. + (pupa_free): If the first free header is not free actually, set + it to P. + + * kern/main.c (pupa_load_normal_mode): Just load the module + "normal". + (pupa_main): Don't print the message + "Entering into rescue mode..." here. + + * include/pupa/i386/pc/loader.h (pupa_rescue_cmd_initrd): + Declared. + (pupa_rescue_cmd_initrd): Likewise. + (pupa_rescue_cmd_initrd): Likewise. + + * include/pupa/symbol.h (FUNCTION): Specify the type. + (VARIABLE): Likewise. + + * include/pupa/err.h (pupa_err_t): Added + PUPA_ERR_UNKNOWN_COMMAND. + + * include/pupa/dl.h (pupa_dl_set_prefix): Exported. + (pupa_dl_get_prefix): Likewise. + + * conf/i386-pc.rmk (pkgdata_MODULES): Added normal.mod. + Added _chain.mod and _linux.mod instead of chain.mod and + linux.mod. + (chain_mod_SOURCES): Renamed to ... + (_chain_mod_SOURCES): ... this. + (chain_mod_CFLAGS): Renamed to ... + (_chain_mod_CFLAGS): ... this. + (linux_mod_SOURCES): Renamed to ... + (_linux_mod_SOURCES): ... this. + (linux_mod_CFLAGS): Renamed to ... + (_linux_mod_CFLAGS): ... this. + (normal_mod_SOURCES): New variable. + (normal_mod_CFLAGS): Likewise. + (normal_mod_ASFLAGS): Likewise. + +2003-01-18 Yoshinori K. Okuji + + * kern/rescue.c (pupa_rescue_cmd_rmmod): Call pupa_dl_unload, if + possible. + + * kern/dl.c (pupa_dl_ref): Refer dependending modules + recursively. + (pupa_dl_unref): Unrefer depending modules recursively. + Don't call pupa_dl_unload implicitly, because PUPA can crash if + a module is unloaded before one depending on that module is + unloaded. + (pupa_dl_unload): Unload depending modules explicitly, + if possible. + 2003-01-17 Yoshinori K. Okuji * include/pupa/i386/pc/linux.h: New file. diff --git a/conf/i386-pc.mk b/conf/i386-pc.mk index af2ae4140..25bd00297 100644 --- a/conf/i386-pc.mk +++ b/conf/i386-pc.mk @@ -393,46 +393,46 @@ genmoddep-util_genmoddep.d: util/genmoddep.c # Modules. -pkgdata_MODULES = chain.mod fat.mod linux.mod +pkgdata_MODULES = _chain.mod _linux.mod fat.mod normal.mod -# For chain.mod. -chain_mod_SOURCES = loader/i386/pc/chainloader.c -CLEANFILES += chain.mod mod-chain.o mod-chain.c pre-chain.o chain_mod-loader_i386_pc_chainloader.o def-chain.lst und-chain.lst -MOSTLYCLEANFILES += chain_mod-loader_i386_pc_chainloader.d -DEFSYMFILES += def-chain.lst -UNDSYMFILES += und-chain.lst +# For _chain.mod. +_chain_mod_SOURCES = loader/i386/pc/chainloader.c +CLEANFILES += _chain.mod mod-_chain.o mod-_chain.c pre-_chain.o _chain_mod-loader_i386_pc_chainloader.o def-_chain.lst und-_chain.lst +MOSTLYCLEANFILES += _chain_mod-loader_i386_pc_chainloader.d +DEFSYMFILES += def-_chain.lst +UNDSYMFILES += und-_chain.lst -chain.mod: pre-chain.o mod-chain.o +_chain.mod: pre-_chain.o mod-_chain.o -rm -f $@ $(LD) -r -o $@ $^ $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R .comment $@ -pre-chain.o: chain_mod-loader_i386_pc_chainloader.o +pre-_chain.o: _chain_mod-loader_i386_pc_chainloader.o -rm -f $@ $(LD) -r -o $@ $^ -mod-chain.o: mod-chain.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -c -o $@ $< +mod-_chain.o: mod-_chain.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -c -o $@ $< -mod-chain.c: moddep.lst genmodsrc.sh - sh $(srcdir)/genmodsrc.sh 'chain' $< > $@ || (rm -f $@; exit 1) +mod-_chain.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh '_chain' $< > $@ || (rm -f $@; exit 1) -def-chain.lst: pre-chain.o - $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 chain/' > $@ +def-_chain.lst: pre-_chain.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 _chain/' > $@ -und-chain.lst: pre-chain.o - echo 'chain' > $@ +und-_chain.lst: pre-_chain.o + echo '_chain' > $@ $(NM) -u -P -p $< >> $@ -chain_mod-loader_i386_pc_chainloader.o: loader/i386/pc/chainloader.c - $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -c -o $@ $< +_chain_mod-loader_i386_pc_chainloader.o: loader/i386/pc/chainloader.c + $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -c -o $@ $< -chain_mod-loader_i386_pc_chainloader.d: loader/i386/pc/chainloader.c - set -e; $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(chain_mod_CFLAGS) -M $< | sed 's,chainloader\.o[ :]*,chain_mod-loader_i386_pc_chainloader.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ +_chain_mod-loader_i386_pc_chainloader.d: loader/i386/pc/chainloader.c + set -e; $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(_chain_mod_CFLAGS) -M $< | sed 's,chainloader\.o[ :]*,_chain_mod-loader_i386_pc_chainloader.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ --include chain_mod-loader_i386_pc_chainloader.d +-include _chain_mod-loader_i386_pc_chainloader.d -chain_mod_CFLAGS = $(COMMON_CFLAGS) +_chain_mod_CFLAGS = $(COMMON_CFLAGS) # For fat.mod. fat_mod_SOURCES = fs/fat.c @@ -473,44 +473,117 @@ fat_mod-fs_fat.d: fs/fat.c fat_mod_CFLAGS = $(COMMON_CFLAGS) -# For linux.mod. -linux_mod_SOURCES = loader/i386/pc/linux.c -CLEANFILES += linux.mod mod-linux.o mod-linux.c pre-linux.o linux_mod-loader_i386_pc_linux.o def-linux.lst und-linux.lst -MOSTLYCLEANFILES += linux_mod-loader_i386_pc_linux.d -DEFSYMFILES += def-linux.lst -UNDSYMFILES += und-linux.lst +# For _linux.mod. +_linux_mod_SOURCES = loader/i386/pc/linux.c +CLEANFILES += _linux.mod mod-_linux.o mod-_linux.c pre-_linux.o _linux_mod-loader_i386_pc_linux.o def-_linux.lst und-_linux.lst +MOSTLYCLEANFILES += _linux_mod-loader_i386_pc_linux.d +DEFSYMFILES += def-_linux.lst +UNDSYMFILES += und-_linux.lst -linux.mod: pre-linux.o mod-linux.o +_linux.mod: pre-_linux.o mod-_linux.o -rm -f $@ $(LD) -r -o $@ $^ $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R .comment $@ -pre-linux.o: linux_mod-loader_i386_pc_linux.o +pre-_linux.o: _linux_mod-loader_i386_pc_linux.o -rm -f $@ $(LD) -r -o $@ $^ -mod-linux.o: mod-linux.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(linux_mod_CFLAGS) -c -o $@ $< +mod-_linux.o: mod-_linux.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(_linux_mod_CFLAGS) -c -o $@ $< -mod-linux.c: moddep.lst genmodsrc.sh - sh $(srcdir)/genmodsrc.sh 'linux' $< > $@ || (rm -f $@; exit 1) +mod-_linux.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh '_linux' $< > $@ || (rm -f $@; exit 1) -def-linux.lst: pre-linux.o - $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 linux/' > $@ +def-_linux.lst: pre-_linux.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 _linux/' > $@ -und-linux.lst: pre-linux.o - echo 'linux' > $@ +und-_linux.lst: pre-_linux.o + echo '_linux' > $@ $(NM) -u -P -p $< >> $@ -linux_mod-loader_i386_pc_linux.o: loader/i386/pc/linux.c - $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(linux_mod_CFLAGS) -c -o $@ $< +_linux_mod-loader_i386_pc_linux.o: loader/i386/pc/linux.c + $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(_linux_mod_CFLAGS) -c -o $@ $< -linux_mod-loader_i386_pc_linux.d: loader/i386/pc/linux.c - set -e; $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(linux_mod_CFLAGS) -M $< | sed 's,linux\.o[ :]*,linux_mod-loader_i386_pc_linux.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ +_linux_mod-loader_i386_pc_linux.d: loader/i386/pc/linux.c + set -e; $(CC) -Iloader/i386/pc -I$(srcdir)/loader/i386/pc $(CPPFLAGS) $(CFLAGS) $(_linux_mod_CFLAGS) -M $< | sed 's,linux\.o[ :]*,_linux_mod-loader_i386_pc_linux.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ --include linux_mod-loader_i386_pc_linux.d +-include _linux_mod-loader_i386_pc_linux.d -linux_mod_CFLAGS = $(COMMON_CFLAGS) +_linux_mod_CFLAGS = $(COMMON_CFLAGS) + +# For normal.mod. +normal_mod_SOURCES = normal/cmdline.c normal/command.c normal/main.c \ + normal/menu.c normal/i386/setjmp.S +CLEANFILES += normal.mod mod-normal.o mod-normal.c pre-normal.o normal_mod-normal_cmdline.o normal_mod-normal_command.o normal_mod-normal_main.o normal_mod-normal_menu.o normal_mod-normal_i386_setjmp.o def-normal.lst und-normal.lst +MOSTLYCLEANFILES += normal_mod-normal_cmdline.d normal_mod-normal_command.d normal_mod-normal_main.d normal_mod-normal_menu.d normal_mod-normal_i386_setjmp.d +DEFSYMFILES += def-normal.lst +UNDSYMFILES += und-normal.lst + +normal.mod: pre-normal.o mod-normal.o + -rm -f $@ + $(LD) -r -o $@ $^ + $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R .comment $@ + +pre-normal.o: normal_mod-normal_cmdline.o normal_mod-normal_command.o normal_mod-normal_main.o normal_mod-normal_menu.o normal_mod-normal_i386_setjmp.o + -rm -f $@ + $(LD) -r -o $@ $^ + +mod-normal.o: mod-normal.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -c -o $@ $< + +mod-normal.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'normal' $< > $@ || (rm -f $@; exit 1) + +def-normal.lst: pre-normal.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 normal/' > $@ + +und-normal.lst: pre-normal.o + echo 'normal' > $@ + $(NM) -u -P -p $< >> $@ + +normal_mod-normal_cmdline.o: normal/cmdline.c + $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -c -o $@ $< + +normal_mod-normal_cmdline.d: normal/cmdline.c + set -e; $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -M $< | sed 's,cmdline\.o[ :]*,normal_mod-normal_cmdline.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include normal_mod-normal_cmdline.d + +normal_mod-normal_command.o: normal/command.c + $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -c -o $@ $< + +normal_mod-normal_command.d: normal/command.c + set -e; $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -M $< | sed 's,command\.o[ :]*,normal_mod-normal_command.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include normal_mod-normal_command.d + +normal_mod-normal_main.o: normal/main.c + $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -c -o $@ $< + +normal_mod-normal_main.d: normal/main.c + set -e; $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -M $< | sed 's,main\.o[ :]*,normal_mod-normal_main.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include normal_mod-normal_main.d + +normal_mod-normal_menu.o: normal/menu.c + $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -c -o $@ $< + +normal_mod-normal_menu.d: normal/menu.c + set -e; $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) $(normal_mod_CFLAGS) -M $< | sed 's,menu\.o[ :]*,normal_mod-normal_menu.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include normal_mod-normal_menu.d + +normal_mod-normal_i386_setjmp.o: normal/i386/setjmp.S + $(CC) -Inormal/i386 -I$(srcdir)/normal/i386 $(CPPFLAGS) $(ASFLAGS) $(normal_mod_ASFLAGS) -c -o $@ $< + +normal_mod-normal_i386_setjmp.d: normal/i386/setjmp.S + set -e; $(CC) -Inormal/i386 -I$(srcdir)/normal/i386 $(CPPFLAGS) $(ASFLAGS) $(normal_mod_ASFLAGS) -M $< | sed 's,setjmp\.o[ :]*,normal_mod-normal_i386_setjmp.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@ + +-include normal_mod-normal_i386_setjmp.d + +normal_mod_CFLAGS = $(COMMON_CFLAGS) +normal_mod_ASFLAGS = $(COMMON_ASFLAGS) CLEANFILES += moddep.lst pkgdata_DATA += moddep.lst moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 132f4189d..847f83bea 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -60,16 +60,22 @@ pupa_setup_SOURCES = util/i386/pc/pupa-setup.c util/i386/pc/biosdisk.c \ genmoddep_SOURCES = util/genmoddep.c # Modules. -pkgdata_MODULES = chain.mod fat.mod linux.mod +pkgdata_MODULES = _chain.mod _linux.mod fat.mod normal.mod -# For chain.mod. -chain_mod_SOURCES = loader/i386/pc/chainloader.c -chain_mod_CFLAGS = $(COMMON_CFLAGS) +# For _chain.mod. +_chain_mod_SOURCES = loader/i386/pc/chainloader.c +_chain_mod_CFLAGS = $(COMMON_CFLAGS) # For fat.mod. fat_mod_SOURCES = fs/fat.c fat_mod_CFLAGS = $(COMMON_CFLAGS) -# For linux.mod. -linux_mod_SOURCES = loader/i386/pc/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) +# For _linux.mod. +_linux_mod_SOURCES = loader/i386/pc/linux.c +_linux_mod_CFLAGS = $(COMMON_CFLAGS) + +# For normal.mod. +normal_mod_SOURCES = normal/cmdline.c normal/command.c normal/main.c \ + normal/menu.c normal/i386/setjmp.S +normal_mod_CFLAGS = $(COMMON_CFLAGS) +normal_mod_ASFLAGS = $(COMMON_ASFLAGS) diff --git a/include/grub/dl.h b/include/grub/dl.h index 954160180..b53b44502 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -83,8 +83,8 @@ pupa_dl_t EXPORT_FUNC(pupa_dl_get) (const char *name); pupa_err_t EXPORT_FUNC(pupa_dl_register_symbol) (const char *name, void *addr, pupa_dl_t mod); void *EXPORT_FUNC(pupa_dl_resolve_symbol) (const char *name); -void pupa_dl_set_prefix (const char *dir); -const char *pupa_dl_get_prefix (void); +void EXPORT_FUNC(pupa_dl_set_prefix) (const char *dir); +const char *EXPORT_FUNC(pupa_dl_get_prefix) (void); int pupa_arch_dl_check_header (void *ehdr, pupa_size_t size); pupa_err_t pupa_arch_dl_relocate_symbols (pupa_dl_t mod, void *ehdr); diff --git a/include/grub/err.h b/include/grub/err.h index 4524fc13e..33bcf6d30 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -40,6 +40,7 @@ typedef enum PUPA_ERR_BAD_DEVICE, PUPA_ERR_READ_ERROR, PUPA_ERR_WRITE_ERROR, + PUPA_ERR_UNKNOWN_COMMAND, PUPA_ERR_BAD_ARGUMENT, PUPA_ERR_BAD_PART_TABLE, PUPA_ERR_UNKNOWN_OS, diff --git a/include/grub/i386/pc/loader.h b/include/grub/i386/pc/loader.h index b81a65c1d..dad25513d 100644 --- a/include/grub/i386/pc/loader.h +++ b/include/grub/i386/pc/loader.h @@ -33,4 +33,10 @@ void EXPORT_FUNC(pupa_linux_boot_bzimage) (void) __attribute__ ((noreturn)); /* This is an asm part of the chainloader. */ void EXPORT_FUNC(pupa_chainloader_real_boot) (int drive, void *part_addr) __attribute__ ((noreturn)); +/* It is necessary to export these functions, because normal mode commands + reuse rescue mode commands. */ +void pupa_rescue_cmd_chainloader (int argc, char *argv[]); +void pupa_rescue_cmd_linux (int argc, char *argv[]); +void pupa_rescue_cmd_initrd (int argc, char *argv[]); + #endif /* ! PUPA_LOADER_MACHINE_HEADER */ diff --git a/include/grub/i386/setjmp.h b/include/grub/i386/setjmp.h new file mode 100644 index 000000000..f6627c1b9 --- /dev/null +++ b/include/grub/i386/setjmp.h @@ -0,0 +1,25 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PUPA_SETJMP_CPU_HEADER +#define PUPA_SETJMP_CPU_HEADER 1 + +typedef unsigned long pupa_jmp_buf[6]; + +#endif /* ! PUPA_SETJMP_CPU_HEADER */ diff --git a/include/grub/normal.h b/include/grub/normal.h new file mode 100644 index 000000000..eac490460 --- /dev/null +++ b/include/grub/normal.h @@ -0,0 +1,131 @@ +/* normal.h - prototypes for the normal mode */ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2002,2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PUPA_NORMAL_HEADER +#define PUPA_NORMAL_HEADER 1 + +#include + +/* The maximum size of a command-line. */ +#define PUPA_MAX_CMDLINE 1600 + +/* Can be run in the command-line. */ +#define PUPA_COMMAND_FLAG_CMDLINE 0x1 +/* Can be run in the menu. */ +#define PUPA_COMMAND_FLAG_MENU 0x2 +/* Can be run in both interfaces. */ +#define PUPA_COMMAND_FLAG_BOTH 0x3 +/* Only for the command title. */ +#define PUPA_COMMAND_FLAG_TITLE 0x4 +/* Don't print the command on booting. */ +#define PUPA_COMMAND_FLAG_NO_ECHO 0x8 + +/* The command description. */ +struct pupa_command +{ + /* The name. */ + const char *name; + + /* The callback function. */ + int (*func) (int argc, char *argv[]); + + /* The flags. */ + unsigned flags; + + /* The summary of the command usage. */ + const char *summary; + + /* The description of the command. */ + const char *description; + + /* The next element. */ + struct pupa_command *next; +}; +typedef struct pupa_command *pupa_command_t; + +/* The command list. */ +struct pupa_command_list +{ + /* The string of a command. */ + const char *command; + + /* The next element. */ + struct pupa_command_list *next; +}; +typedef struct pupa_command_list *pupa_command_list_t; + +/* The menu entry. */ +struct pupa_menu_entry +{ + /* The title name. */ + const char *title; + + /* The number of commands. */ + int num; + + /* The list of commands. */ + pupa_command_list_t command_list; + + /* The next element. */ + struct pupa_menu_entry *next; +}; +typedef struct pupa_menu_entry *pupa_menu_entry_t; + +/* The menu. */ +struct pupa_menu +{ + /* The default entry number. */ + int default_entry; + + /* The fallback entry number. */ + int fallback_entry; + + /* The timeout to boot the default entry automatically. */ + int timeout; + + /* The size of a menu. */ + int size; + + /* The list of menu entries. */ + pupa_menu_entry_t entry_list; +}; +typedef struct pupa_menu *pupa_menu_t; + +/* To exit from the normal mode. */ +extern pupa_jmp_buf pupa_exit_env; + +void pupa_enter_normal_mode (const char *config); +void pupa_normal_execute (const char *config, int nested); +void pupa_menu_run (pupa_menu_t menu, int nested); +void pupa_cmdline_run (int nested); +int pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, + int echo_char, int readline); +void pupa_register_command (const char *name, + int (*func) (int argc, char *argv[]), + unsigned flags, + const char *summary, + const char *description); +void pupa_unregister_command (const char *name); +pupa_command_t pupa_command_find (char *cmdline); +int pupa_command_execute (char *cmdline); +void pupa_command_init (void); +void pupa_normal_init_page (void); + +#endif /* ! PUPA_NORMAL_HEADER */ diff --git a/include/grub/setjmp.h b/include/grub/setjmp.h new file mode 100644 index 000000000..35c05a5a5 --- /dev/null +++ b/include/grub/setjmp.h @@ -0,0 +1,29 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PUPA_SETJMP_HEADER +#define PUPA_SETJMP_HEADER 1 + +/* This must define pupa_jmp_buf. */ +#include + +int pupa_setjmp (pupa_jmp_buf env); +void pupa_longjmp (pupa_jmp_buf env, int val) __attribute__ ((noreturn)); + +#endif /* ! PUPA_SETJMP_HEADER */ diff --git a/include/grub/symbol.h b/include/grub/symbol.h index c51df2600..d3a4cbc4e 100644 --- a/include/grub/symbol.h +++ b/include/grub/symbol.h @@ -30,8 +30,8 @@ # define EXT_C(sym) sym #endif -#define FUNCTION(x) .globl EXT_C(x) ; EXT_C(x): -#define VARIABLE(x) FUNCTION(x) +#define FUNCTION(x) .globl EXT_C(x) ; .type EXT_C(x), @function ; EXT_C(x): +#define VARIABLE(x) .globl EXT_C(x) ; .type EXT_C(x), @object ; EXT_C(x): /* Mark an exported symbol. */ #define EXPORT_FUNC(x) x diff --git a/kern/dl.c b/kern/dl.c index 7066aded7..21b702d97 100644 --- a/kern/dl.c +++ b/kern/dl.c @@ -459,19 +459,23 @@ pupa_dl_resolve_dependencies (pupa_dl_t mod, Elf_Ehdr *e) int pupa_dl_ref (pupa_dl_t mod) { + pupa_dl_dep_t dep; + + for (dep = mod->dep; dep; dep = dep->next) + pupa_dl_ref (dep->mod); + return ++mod->ref_count; } int pupa_dl_unref (pupa_dl_t mod) { - int ret; + pupa_dl_dep_t dep; - ret = --mod->ref_count; - if (ret <= 0) - pupa_dl_unload (mod); - - return ret; + for (dep = mod->dep; dep; dep = dep->next) + pupa_dl_unref (dep->mod); + + return --mod->ref_count; } /* Load a module from core memory. */ @@ -605,7 +609,10 @@ pupa_dl_unload (pupa_dl_t mod) for (dep = mod->dep; dep; dep = depn) { depn = dep->next; - pupa_dl_unref (dep->mod); + + if (! pupa_dl_unref (dep->mod)) + pupa_dl_unload (dep->mod); + pupa_free (dep); } diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S index cafcc3810..f2cb6e902 100644 --- a/kern/i386/pc/startup.S +++ b/kern/i386/pc/startup.S @@ -1080,6 +1080,50 @@ FUNCTION(pupa_console_putchar) * %al = ASCII character */ +/* this table is used in translate_keycode below */ +translation_table: + .word PUPA_CONSOLE_KEY_LEFT, 2 + .word PUPA_CONSOLE_KEY_RIGHT, 6 + .word PUPA_CONSOLE_KEY_UP, 16 + .word PUPA_CONSOLE_KEY_DOWN, 14 + .word PUPA_CONSOLE_KEY_HOME, 1 + .word PUPA_CONSOLE_KEY_END, 5 + .word PUPA_CONSOLE_KEY_DC, 4 + .word PUPA_CONSOLE_KEY_BACKSPACE, 8 + .word PUPA_CONSOLE_KEY_PPAGE, 7 + .word PUPA_CONSOLE_KEY_NPAGE, 3 + .word 0 + +/* + * translate_keycode translates the key code %dx to an ascii code. + */ + .code16 + +translate_keycode: + pushw %bx + pushw %si + + movw $ABS(translation_table), %si + +1: lodsw + /* check if this is the end */ + testw %ax, %ax + jz 2f + /* load the ascii code into %ax */ + movw %ax, %bx + lodsw + /* check if this matches the key code */ + cmpw %bx, %dx + jne 1b + /* translate %dx, if successful */ + movw %ax, %dx + +2: popw %si + popw %bx + ret + + .code32 + FUNCTION(pupa_console_getkey) pushl %ebp @@ -1089,7 +1133,8 @@ FUNCTION(pupa_console_getkey) int $0x16 movw %ax, %dx /* real_to_prot uses %eax */ - + call translate_keycode + DATA32 call real_to_prot .code32 diff --git a/kern/main.c b/kern/main.c index a870b84e4..ac0e9376e 100644 --- a/kern/main.c +++ b/kern/main.c @@ -84,34 +84,8 @@ pupa_set_root_dev (void) static void pupa_load_normal_mode (void) { - if (pupa_dl_load ("normal")) - { - void (*normal_func) (const char *config); - - /* If the function pupa_enter_normal_mode is present, call it. */ - normal_func = pupa_dl_resolve_symbol ("pupa_enter_normal_mode"); - if (normal_func) - { - char *config; - char *prefix; - - prefix = pupa_dl_get_prefix (); - if (! prefix) - pupa_fatal ("The dl prefix is not set!"); - - config = pupa_malloc (pupa_strlen (prefix) + sizeof ("/pupa.cfg")); - if (! config) - pupa_fatal ("out of memory"); - - pupa_sprintf (config, "%s/pupa.cfg", prefix); - (*normal_func) (config); - pupa_free (config); - } - else - pupa_printf ("No entrance routine in the normal mode!\n"); - } - else - pupa_printf ("Failed to load the normal mode.\n"); + /* Load the module. */ + pupa_dl_load ("normal"); /* Ignore any error, because we have the rescue mode anyway. */ pupa_errno = PUPA_ERR_NONE; @@ -138,10 +112,9 @@ pupa_main (void) pupa_load_modules (); pupa_add_unused_region (); - /* Go to the real world. */ + /* Load the normal mode module. */ pupa_load_normal_mode (); - /* If pupa_enter_normal_mode fails or doesn't exist, enter rescue mode. */ - pupa_printf ("Entering into rescue mode...\n"); + /* Enter the rescue mode. */ pupa_enter_rescue_mode (); } diff --git a/kern/mm.c b/kern/mm.c index 7ba30fcb0..6eba8d3d8 100644 --- a/kern/mm.c +++ b/kern/mm.c @@ -151,7 +151,7 @@ pupa_real_malloc (pupa_mm_header_t *first, pupa_size_t n, pupa_size_t align) pupa_fatal ("null in the ring"); if (p->magic != PUPA_MM_FREE_MAGIC) - pupa_fatal ("free magic is broken at %p", p); + pupa_fatal ("free magic is broken at %p: 0x%x", p, p->magic); if (p->size >= n + extra) { @@ -184,6 +184,7 @@ pupa_real_malloc (pupa_mm_header_t *first, pupa_size_t n, pupa_size_t align) } *first = q; + return p + 1; } @@ -259,19 +260,30 @@ pupa_free (void *ptr) get_header_from_pointer (ptr, &p, &r); - if (p == r->first) + if (r->first->magic == PUPA_MM_ALLOC_MAGIC) { p->magic = PUPA_MM_FREE_MAGIC; - p->next = p; + r->first = p->next = p; } else { pupa_mm_header_t q; + +#if 0 + q = r->first; + do + { + pupa_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n", + __FILE__, __LINE__, q, q->size, q->magic); + q = q->next; + } + while (q != r->first); +#endif for (q = r->first; q >= p || q->next <= p; q = q->next) { if (q->magic != PUPA_MM_FREE_MAGIC) - pupa_fatal ("free magic is broken at %p", q); + pupa_fatal ("free magic is broken at %p: 0x%x", q, q->magic); if (q >= q->next && (q < p || q->next > p)) break; diff --git a/kern/rescue.c b/kern/rescue.c index 36b197998..51e80d579 100644 --- a/kern/rescue.c +++ b/kern/rescue.c @@ -560,7 +560,8 @@ pupa_rescue_cmd_rmmod (int argc, char *argv[]) return; } - pupa_dl_unref (mod); + if (! pupa_dl_unref (mod)) + pupa_dl_unload (mod); } /* lsmod */ @@ -590,10 +591,30 @@ pupa_rescue_cmd_lsmod (int argc __attribute__ ((unused)), pupa_dl_iterate (print_module); } +static void +attempt_normal_mode (void) +{ + pupa_rescue_command_t cmd; + + for (cmd = pupa_rescue_command_list; cmd; cmd = cmd->next) + { + if (pupa_strcmp ("normal", cmd->name) == 0) + { + (cmd->func) (0, 0); + break; + } + } +} + /* Enter the rescue mode. */ void pupa_enter_rescue_mode (void) { + /* First of all, attempt to execute the normal mode. */ + attempt_normal_mode (); + + pupa_printf ("Entering into rescue mode...\n"); + pupa_rescue_register_command ("boot", pupa_rescue_cmd_boot, "boot an operating system"); pupa_rescue_register_command ("cat", pupa_rescue_cmd_cat, diff --git a/loader/i386/pc/chainloader.c b/loader/i386/pc/chainloader.c index 5562bb289..b0d417da2 100644 --- a/loader/i386/pc/chainloader.c +++ b/loader/i386/pc/chainloader.c @@ -81,7 +81,7 @@ pupa_chainloader_unload (void) return PUPA_ERR_NONE; } -static void +void pupa_rescue_cmd_chainloader (int argc, char *argv[]) { pupa_file_t file = 0; diff --git a/loader/i386/pc/linux.c b/loader/i386/pc/linux.c index 8dbffe8b4..b8fb77334 100644 --- a/loader/i386/pc/linux.c +++ b/loader/i386/pc/linux.c @@ -59,7 +59,7 @@ pupa_linux_unload (void) return PUPA_ERR_NONE; } -static void +void pupa_rescue_cmd_linux (int argc, char *argv[]) { pupa_file_t file = 0; @@ -282,7 +282,7 @@ pupa_rescue_cmd_linux (int argc, char *argv[]) } } -static void +void pupa_rescue_cmd_initrd (int argc, char *argv[]) { pupa_error (PUPA_ERR_NOT_IMPLEMENTED_YET, "not implemented yet"); diff --git a/normal/cmdline.c b/normal/cmdline.c new file mode 100644 index 000000000..b021b6a1d --- /dev/null +++ b/normal/cmdline.c @@ -0,0 +1,298 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. + * Copyright (C) 2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +static char *kill_buf; + +void +pupa_cmdline_run (int nested) +{ + pupa_normal_init_page (); + + pupa_printf ("\ + [ Minimal BASH-like line editing is supported. For the first word, TAB\n\ + lists possible command completions. Anywhere else TAB lists possible\n\ + device/file completions.%s ]\n\n", + nested ? " ESC at any time exits." : ""); + + while (1) + { + static char cmdline[PUPA_MAX_CMDLINE]; + pupa_command_t cmd; + + pupa_print_error (); + pupa_errno = PUPA_ERR_NONE; + cmdline[0] = '\0'; + + if (! pupa_cmdline_get ("pupa> ", cmdline, sizeof (cmdline), 0, 1) + && nested) + return; + + if (! *cmdline) + continue; + + cmd = pupa_command_find (cmdline); + if (! cmd) + continue; + + if (! (cmd->flags & PUPA_COMMAND_FLAG_CMDLINE)) + { + pupa_error (PUPA_ERR_UNKNOWN_COMMAND, + "invalid command `%s'", cmd->name); + continue; + } + + pupa_command_execute (cmdline); + } +} + +/* Get a command-line. If ECHO_CHAR is not zero, echo it instead of input + characters. If READLINE is non-zero, readline-like key bindings are + available. If ESC is pushed, return non-zero, otherwise return zero. */ +/* FIXME: The dumb interface is not supported yet. */ +int +pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, + int echo_char, int readline) +{ + unsigned xpos, ypos, ystart; + pupa_size_t lpos, llen; + pupa_size_t plen; + char buf[max_len]; + int key; + auto void cl_insert (const char *str); + auto void cl_delete (unsigned len); + auto void cl_print (int pos, int c); + auto void cl_set_pos (void); + + void cl_set_pos (void) + { + xpos = (plen + lpos) % 79; + ypos = ystart + (plen + lpos) / 79; + pupa_gotoxy (xpos, ypos); + } + + void cl_print (int pos, int c) + { + char *p; + + for (p = buf + pos; *p; p++) + { + if (xpos++ > 78) + { + pupa_putchar ('\n'); + + xpos = 1; + if (ypos == (unsigned) (pupa_getxy () & 0xFF)) + ystart--; + else + ypos++; + } + + if (c) + pupa_putchar (c); + else + pupa_putchar (*p); + } + } + + void cl_insert (const char *str) + { + pupa_size_t len = pupa_strlen (str); + + if (len + llen < max_len) + { + pupa_memmove (buf + lpos + len, buf + lpos, llen - lpos + 1); + pupa_memmove (buf + lpos, str, len); + + llen += len; + lpos += len; + cl_print (lpos - len, echo_char); + cl_set_pos (); + } + } + + void cl_delete (unsigned len) + { + if (lpos + len <= llen) + { + pupa_size_t saved_lpos = lpos; + + lpos = llen - len; + cl_set_pos (); + cl_print (lpos, ' '); + lpos = saved_lpos; + cl_set_pos (); + + pupa_memmove (buf + lpos, buf + lpos + len, llen - lpos + 1); + llen -= len; + cl_print (lpos, echo_char); + cl_set_pos (); + } + } + + plen = pupa_strlen (prompt); + lpos = llen = 0; + buf[0] = '\0'; + + if ((pupa_getxy () >> 8) != 0) + pupa_putchar ('\n'); + + pupa_printf (prompt); + + xpos = plen; + ystart = ypos = (pupa_getxy () & 0xFF); + + cl_insert (cmdline); + + while ((key = PUPA_TERM_ASCII_CHAR (pupa_getkey ())) != '\n' && key != '\r') + { + if (readline) + { + switch (key) + { + case 1: /* Ctrl-a */ + lpos = 0; + cl_set_pos (); + break; + + case 2: /* Ctrl-b */ + if (lpos > 0) + { + lpos--; + cl_set_pos (); + } + break; + + case 5: /* Ctrl-e */ + lpos = llen; + cl_set_pos (); + break; + + case 6: /* Ctrl-f */ + if (lpos < llen) + { + lpos++; + cl_set_pos (); + } + break; + + case 9: /* Ctrl-i or TAB */ + /* FIXME */ + break; + + case 11: /* Ctrl-k */ + if (lpos < llen) + { + if (kill_buf) + pupa_free (kill_buf); + + kill_buf = pupa_strdup (buf + lpos); + pupa_errno = PUPA_ERR_NONE; + + cl_delete (llen - lpos); + } + break; + + case 14: /* Ctrl-n */ + /* FIXME */ + break; + + case 16: /* Ctrl-p */ + /* FIXME */ + break; + + case 21: /* Ctrl-u */ + if (lpos > 0) + { + pupa_size_t n = lpos; + + if (kill_buf) + pupa_free (kill_buf); + + kill_buf = pupa_malloc (n + 1); + pupa_errno = PUPA_ERR_NONE; + if (kill_buf) + { + pupa_memcpy (kill_buf, buf, n); + kill_buf[n] = '\0'; + } + + lpos = 0; + cl_set_pos (); + cl_delete (n); + } + break; + + case 25: /* Ctrl-y */ + if (kill_buf) + cl_insert (kill_buf); + break; + } + } + + switch (key) + { + case '\e': + return 0; + + case '\b': + if (lpos > 0) + { + lpos--; + cl_set_pos (); + } + /* fall through */ + + case 4: /* Ctrl-d */ + if (lpos < llen) + cl_delete (1); + break; + + default: + if (pupa_isprint (key)) + { + char str[2]; + + str[0] = key; + str[1] = '\0'; + cl_insert (str); + } + break; + } + } + + pupa_putchar ('\n'); + + /* If ECHO_CHAR is NUL, remove leading spaces. */ + lpos = 0; + if (! echo_char) + while (buf[lpos] == ' ') + lpos++; + + pupa_memcpy (cmdline, buf + lpos, llen - lpos + 1); + + return 1; +} diff --git a/normal/command.c b/normal/command.c new file mode 100644 index 000000000..ab148ae81 --- /dev/null +++ b/normal/command.c @@ -0,0 +1,176 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +static pupa_command_t pupa_command_list; + +void +pupa_register_command (const char *name, + int (*func) (int argc, char *argv[]), + unsigned flags, + const char *summary, + const char *description) +{ + pupa_command_t cmd, *p; + + cmd = (pupa_command_t) pupa_malloc (sizeof (*cmd)); + if (! cmd) + return; + + cmd->name = name; + cmd->func = func; + cmd->flags = flags; + cmd->summary = summary; + cmd->description = description; + + /* Keep the list sorted for simplicity. */ + p = &pupa_command_list; + while (*p) + { + if (pupa_strcmp ((*p)->name, name) > 0) + break; + + p = &((*p)->next); + } + + cmd->next = *p; + *p = cmd; +} + +void +pupa_unregister_command (const char *name) +{ + pupa_command_t *p, q; + + for (p = &pupa_command_list, q = *p; q; p = &(q->next), q = q->next) + if (pupa_strcmp (name, q->name) == 0) + { + *p = q->next; + pupa_free (q); + break; + } +} + +pupa_command_t +pupa_command_find (char *cmdline) +{ + char *first_space; + pupa_command_t cmd; + + first_space = pupa_strchr (cmdline, ' '); + if (first_space) + *first_space = '\0'; + + for (cmd = pupa_command_list; cmd; cmd = cmd->next) + if (pupa_strcmp (cmdline, cmd->name) == 0) + break; + + if (! cmd) + pupa_error (PUPA_ERR_UNKNOWN_COMMAND, "unknown command `%s'", cmdline); + + if (first_space) + *first_space = ' '; + + return cmd; +} + +int +pupa_command_execute (char *cmdline) +{ + pupa_command_t cmd; + char *p; + char **args; + int num = 0; + int i; + int ret; + + cmd = pupa_command_find (cmdline); + if (! cmd) + return -1; + + /* Count arguments. */ + p = cmdline; + while (1) + { + while (*p && *p != ' ') + p++; + + if (! *p) + break; + + while (*p == ' ') + p++; + + num++; + } + + args = (char **) pupa_malloc (sizeof (char *) * (num + 1)); + if (! args) + return -1; + + /* Fill arguments. */ + for (i = 0, p = pupa_strchr (cmdline, ' '); i < num && p; i++) + { + if (! p) + break; + + while (*p == ' ') + p++; + + args[i] = p; + + while (*p && *p != ' ') + p++; + + *p++ = '\0'; + } + + /* Terminate the array with NULL. */ + args[i] = 0; + + ret = (cmd->func) (num, args); + + pupa_free (args); + return ret; +} + +static int +rescue_command (int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + pupa_longjmp (pupa_exit_env, 0); + + /* Never reach here. */ + return 0; +} + +void +pupa_command_init (void) +{ + /* This is a special command, because this never be called actually. */ + pupa_register_command ("title", 0, PUPA_COMMAND_FLAG_TITLE, 0, 0); + + pupa_register_command ("rescue", rescue_command, PUPA_COMMAND_FLAG_BOTH, + "rescue", + "Enter into the rescue mode."); +} diff --git a/normal/i386/setjmp.S b/normal/i386/setjmp.S new file mode 100644 index 000000000..0273b0f9e --- /dev/null +++ b/normal/i386/setjmp.S @@ -0,0 +1,57 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + + .file "setjmp.S" + + .text + +/* + * int pupa_setjmp (pupa_jmp_buf env) + */ +FUNCTION(pupa_setjmp) + movl %ebx, 0(%eax) /* EBX */ + movl %esi, 4(%eax) /* ESI */ + movl %edi, 8(%eax) /* EDI */ + movl %ebp, 12(%eax) /* EBP */ + popl %ecx + movl %esp, 16(%eax) /* ESP */ + movl %ecx, 20(%eax) /* EIP */ + xorl %eax, %eax + jmp *%ecx + + +/* + * int pupa_longjmp (pupa_jmp_buf env, int val) + */ +FUNCTION(pupa_longjmp) + movl 0(%eax), %ebx + movl 4(%eax), %esi + movl 8(%eax), %edi + movl 12(%eax), %ebp + movl 16(%eax), %esp + movl 20(%eax), %ecx + + movl %edx, %eax + testl %eax, %eax + jnz 1f + incl %eax +1: jmp *%ecx + \ No newline at end of file diff --git a/normal/main.c b/normal/main.c new file mode 100644 index 000000000..d315b585d --- /dev/null +++ b/normal/main.c @@ -0,0 +1,351 @@ +/* main.c - the normal mode main routine */ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + * Copyright (C) 2002,2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +pupa_jmp_buf pupa_exit_env; + +/* Read a line from the file FILE. */ +static int +get_line (pupa_file_t file, char cmdline[], int max_len) +{ + char c; + int pos = 0; + int literal = 0; + int comment = 0; + + while (1) + { + if (pupa_file_read (file, &c, 1) != 1) + break; + + /* Skip all carriage returns. */ + if (c == '\r') + continue; + + /* Replace tabs with spaces. */ + if (c == '\t') + c = ' '; + + /* The previous is a backslash, then... */ + if (literal) + { + /* If it is a newline, replace it with a space and continue. */ + if (c == '\n') + { + c = ' '; + + /* Go back to overwrite the backslash. */ + if (pos > 0) + pos--; + } + + literal = 0; + } + + if (c == '\\') + literal = 1; + + if (comment) + { + if (c == '\n') + comment = 0; + } + else if (pos == 0) + { + if (c == '#') + comment = 1; + else if (! pupa_isspace (c)) + cmdline[pos++] = c; + } + else + { + if (c == '\n') + break; + + if (pos < max_len) + cmdline[pos++] = c; + } + } + + cmdline[pos] = '\0'; + + return pos; +} + +static void +free_menu (pupa_menu_t menu) +{ + pupa_menu_entry_t entry = menu->entry_list; + + while (entry) + { + pupa_menu_entry_t next_entry = entry->next; + pupa_command_list_t cmd = entry->command_list; + + while (cmd) + { + pupa_command_list_t next_cmd = cmd->next; + + pupa_free ((void *) cmd->command); + cmd = next_cmd; + } + + pupa_free ((void *) entry->title); + entry = next_entry; + } + + pupa_free (menu); +} + +/* Read the config file CONFIG and return a menu. If no entry is present, + return NULL. */ +static pupa_menu_t +read_config_file (const char *config) +{ + pupa_file_t file; + static char cmdline[PUPA_MAX_CMDLINE]; + pupa_menu_t menu; + pupa_menu_entry_t *next_entry, cur_entry = 0; + pupa_command_list_t *next_cmd, cur_cmd; + + /* Try to open the config file. */ + file = pupa_file_open (config); + if (! file) + return 0; + + /* Initialize the menu. */ + menu = (pupa_menu_t) pupa_malloc (sizeof (*menu)); + if (! menu) + { + pupa_file_close (file); + return 0; + } + menu->default_entry = 0; + menu->fallback_entry = -1; + menu->timeout = -1; + menu->size = 0; + menu->entry_list = 0; + + next_entry = &(menu->entry_list); + next_cmd = 0; + + /* Read each line. */ + while (get_line (file, cmdline, sizeof (cmdline))) + { + pupa_command_t cmd; + + cmd = pupa_command_find (cmdline); + pupa_errno = PUPA_ERR_NONE; + if (! cmd) + { + pupa_printf ("Unknown command `%s' is ignored.\n", cmdline); + continue; + } + + if (cmd->flags & PUPA_COMMAND_FLAG_TITLE) + { + char *p; + + cur_entry = (pupa_menu_entry_t) pupa_malloc (sizeof (*cur_entry)); + if (! cur_entry) + goto fail; + + p = pupa_strchr (cmdline, ' '); + if (p) + cur_entry->title = pupa_strdup (p); + else + cur_entry->title = pupa_strdup (""); + + if (! cur_entry->title) + { + pupa_free (cur_entry); + goto fail; + } + + cur_entry->num = 0; + cur_entry->command_list = 0; + cur_entry->next = 0; + + *next_entry = cur_entry; + next_entry = &(cur_entry->next); + + next_cmd = &(cur_entry->command_list); + + menu->size++; + } + else if (! cur_entry) + { + /* Run the command if possible. */ + if (cmd->flags & PUPA_COMMAND_FLAG_MENU) + { + pupa_command_execute (cmdline); + pupa_print_error (); + pupa_errno = PUPA_ERR_NONE; + } + else + { + pupa_printf ("Invalid command `%s' is ignored.\n", cmdline); + continue; + } + } + else + { + cur_cmd = (pupa_command_list_t) pupa_malloc (sizeof (*cur_cmd)); + if (! cur_cmd) + goto fail; + + cur_cmd->command = pupa_strdup (cmdline); + if (! cur_cmd->command) + { + pupa_free (cur_cmd); + goto fail; + } + + cur_cmd->next = 0; + + *next_cmd = cur_cmd; + next_cmd = &(cur_cmd->next); + + cur_entry->num++; + } + } + + fail: + + pupa_file_close (file); + + /* If no entry was found or any error occurred, return NULL. */ + if (menu->size == 0 || pupa_errno != PUPA_ERR_NONE) + { + free_menu (menu); + return 0; + } + + /* Check values of the default entry and the fallback one. */ + if (menu->fallback_entry >= menu->size) + menu->fallback_entry = -1; + + if (menu->default_entry < 0 || menu->default_entry >= menu->size) + { + if (menu->fallback_entry < 0) + menu->default_entry = 0; + else + { + menu->default_entry = menu->fallback_entry; + menu->fallback_entry = -1; + } + } + + return menu; +} + +/* This starts the normal mode. */ +void +pupa_enter_normal_mode (const char *config) +{ + if (pupa_setjmp (pupa_exit_env) == 0) + pupa_normal_execute (config, 0); +} + +/* Initialize the screen. */ +void +pupa_normal_init_page (void) +{ + pupa_cls (); + pupa_printf ("\n\ + PUPA version %s\n\n", + PACKAGE_VERSION); +} + +/* Read the config file CONFIG and execute the menu interface or + the command-line interface. */ +void +pupa_normal_execute (const char *config, int nested) +{ + pupa_menu_t menu = 0; + + if (config) + { + menu = read_config_file (config); + + /* Ignore any error. */ + pupa_errno = PUPA_ERR_NONE; + } + + if (menu) + pupa_menu_run (menu, nested); + else + pupa_cmdline_run (nested); +} + +/* Enter normal mode from rescue mode. */ +static void +pupa_rescue_cmd_normal (int argc, char *argv[]) +{ + if (argc == 0) + { + /* Guess the config filename. */ + char *config; + const char *prefix; + + prefix = pupa_dl_get_prefix (); + if (prefix) + { + config = pupa_malloc (pupa_strlen (prefix) + sizeof ("/pupa.cfg")); + if (! config) + return; + + pupa_sprintf (config, "%s/pupa.cfg", prefix); + pupa_enter_normal_mode (config); + pupa_free (config); + } + else + pupa_enter_normal_mode (0); + } + else + pupa_enter_normal_mode (argv[0]); +} + +PUPA_MOD_INIT +{ + /* Normal mode shouldn't be unloaded. */ + pupa_dl_ref (mod); + + /* Register a command "normal" for the rescue mode. */ + pupa_rescue_register_command ("normal", pupa_rescue_cmd_normal, + "enter normal mode"); + + /* This registers some built-in commands. */ + pupa_command_init (); +} + +PUPA_MOD_FINI +{ + pupa_rescue_unregister_command ("normal"); +} diff --git a/normal/menu.c b/normal/menu.c new file mode 100644 index 000000000..65ab2934c --- /dev/null +++ b/normal/menu.c @@ -0,0 +1,272 @@ +/* + * PUPA -- Preliminary Universal Programming Architecture for GRUB + * Copyright (C) 2003 Yoshinori K. Okuji + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +/* FIXME: These below are all runaround. */ + +#define DISP_UP 0x18 +#define DISP_DOWN 0x19 +#define DISP_RIGHT 0x1a +#define DISP_HLINE 0xc4 +#define DISP_VLINE 0xb3 +#define DISP_UL 0xda +#define DISP_UR 0xbf +#define DISP_LL 0xc0 +#define DISP_LR 0xd9 + +static void +draw_border (void) +{ + unsigned i; + + pupa_setcolorstate (PUPA_TERM_COLOR_NORMAL); + + pupa_gotoxy (1, 3); + pupa_putchar (DISP_UL); + for (i = 0; i < 73; i++) + pupa_putchar (DISP_HLINE); + pupa_putchar (DISP_UR); + + i = 1; + while (1) + { + pupa_gotoxy (1, 3 + i); + + if (i > 12) + break; + + pupa_putchar (DISP_VLINE); + pupa_gotoxy (75, 3 + i); + pupa_putchar (DISP_VLINE); + + i++; + } + + pupa_putchar (DISP_LL); + for (i = 0; i < 73; i++) + pupa_putchar (DISP_HLINE); + pupa_putchar (DISP_LR); + + pupa_setcolorstate (PUPA_TERM_COLOR_STANDARD); +} + +static void +print_message (int nested) +{ + pupa_printf ("\n\ + Use the %c and %c keys to select which entry is highlighted.\n", + DISP_UP, DISP_DOWN); + pupa_printf ("\ + Press enter to boot the selected OS, \'e\' to edit the\n\ + commands before booting, or \'c\' for a command-line."); + if (nested) + pupa_printf ("\ + ESC to return previous menu."); +} + +static pupa_menu_entry_t +get_entry (pupa_menu_t menu, int no) +{ + pupa_menu_entry_t e; + + for (e = menu->entry_list; e && no > 0; e = e->next, no--) + ; + + return e; +} + +static void +print_entry (int y, int highlight, pupa_menu_entry_t entry) +{ + int x; + const char *title; + + title = entry ? entry->title : ""; + + pupa_setcolorstate (highlight + ? PUPA_TERM_COLOR_HIGHLIGHT + : PUPA_TERM_COLOR_NORMAL); + + pupa_gotoxy (2, y); + pupa_putchar (' '); + for (x = 3; x < 75; x++) + { + if (*title && x <= 72) + { + if (x == 72) + pupa_putchar (DISP_RIGHT); + else + pupa_putchar (*title++); + } + else + pupa_putchar (' '); + } + pupa_gotoxy (74, y); + + pupa_setcolorstate (PUPA_TERM_COLOR_STANDARD); +} + +static void +print_entries (pupa_menu_t menu, int first, int offset) +{ + pupa_menu_entry_t e; + int i; + + pupa_gotoxy (77, 4); + + if (first) + pupa_putchar (DISP_UP); + else + pupa_putchar (' '); + + e = get_entry (menu, first); + + for (i = 0; i < 12; i++) + { + print_entry (4 + i, offset == i, e); + if (e) + e = e->next; + } + + pupa_gotoxy (77, 4 + 12); + + if (e) + pupa_putchar (DISP_DOWN); + else + pupa_putchar (' '); + + pupa_gotoxy (74, 4 + offset); +} + +static void +init_page (int nested) +{ + pupa_normal_init_page (); + draw_border (); + print_message (nested); +} + +static int +run_menu (pupa_menu_t menu, int nested) +{ + int first, offset; + + pupa_setcursor (0); + + first = 0; + offset = menu->default_entry; + if (offset > 11) + { + first = offset - 11; + offset = 11; + } + + init_page (nested); + print_entries (menu, first, offset); + + while (1) + { + int c; + + c = PUPA_TERM_ASCII_CHAR (pupa_getkey ()); + switch (c) + { + case 16: + case '^': + if (offset > 0) + { + print_entry (4 + offset, 0, get_entry (menu, first + offset)); + offset--; + print_entry (4 + offset, 1, get_entry (menu, first + offset)); + } + else if (first > 0) + { + first--; + print_entries (menu, first, offset); + } + break; + + case 14: + case 'v': + if (menu->size > first + offset + 1) + { + if (offset < 11) + { + print_entry (4 + offset, 0, + get_entry (menu, first + offset)); + offset++; + print_entry (4 + offset, 1, + get_entry (menu, first + offset)); + } + else + { + first++; + print_entries (menu, first, offset); + } + } + break; + + case '\n': + case '\r': + case 6: + pupa_setcursor (1); + return first + offset; + + case '\e': + if (nested) + { + pupa_setcursor (1); + return -1; + } + break; + + case 'c': + pupa_setcursor (1); + pupa_cmdline_run (1); + pupa_setcursor (0); + init_page (nested); + print_entries (menu, first, offset); + break; + + default: + break; + } + } + + /* Never reach here. */ + return -1; +} + +void +pupa_menu_run (pupa_menu_t menu, int nested) +{ + while (1) + { + int boot_entry; + + boot_entry = run_menu (menu, nested); + if (boot_entry < 0) + break; + + /* FIXME: Boot the entry. */ + } +}