mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
116
arch/alpha/boot/Makefile
Normal file
116
arch/alpha/boot/Makefile
Normal file
|
@ -0,0 +1,116 @@
|
|||
#
|
||||
# arch/alpha/boot/Makefile
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU General Public
|
||||
# License. See the file "COPYING" in the main directory of this archive
|
||||
# for more details.
|
||||
#
|
||||
# Copyright (C) 1994 by Linus Torvalds
|
||||
#
|
||||
|
||||
hostprogs-y := tools/mkbb tools/objstrip
|
||||
targets := vmlinux.gz vmlinux \
|
||||
vmlinux.nh tools/lxboot tools/bootlx tools/bootph \
|
||||
tools/bootpzh bootloader bootpheader bootpzheader
|
||||
OBJSTRIP := $(obj)/tools/objstrip
|
||||
|
||||
# SRM bootable image. Copy to offset 512 of a partition.
|
||||
$(obj)/bootimage: $(addprefix $(obj)/tools/,mkbb lxboot bootlx) $(obj)/vmlinux.nh
|
||||
( cat $(obj)/tools/lxboot $(obj)/tools/bootlx $(obj)/vmlinux.nh ) > $@
|
||||
$(obj)/tools/mkbb $@ $(obj)/tools/lxboot
|
||||
@echo ' Bootimage $@ is ready'
|
||||
|
||||
# BOOTP bootable image. Define INITRD during make to append initrd image.
|
||||
$(obj)/bootpfile: $(obj)/tools/bootph $(obj)/vmlinux.nh
|
||||
cat $(obj)/tools/bootph $(obj)/vmlinux.nh > $@
|
||||
ifdef INITRD
|
||||
cat $(INITRD) >> $@
|
||||
endif
|
||||
|
||||
# Compressed kernel BOOTP bootable image.
|
||||
# Define INITRD during make to append initrd image.
|
||||
$(obj)/bootpzfile: $(obj)/tools/bootpzh $(obj)/vmlinux.nh.gz
|
||||
cat $(obj)/tools/bootpzh $(obj)/vmlinux.nh.gz > $@
|
||||
ifdef INITRD
|
||||
cat $(INITRD) >> $@
|
||||
endif
|
||||
|
||||
# Compressed kernel image
|
||||
$(obj)/vmlinux.gz: $(obj)/vmlinux FORCE
|
||||
$(call if_changed,gzip)
|
||||
@echo ' Kernel $@ is ready'
|
||||
|
||||
$(obj)/main.o: $(obj)/ksize.h
|
||||
$(obj)/bootp.o: $(obj)/ksize.h
|
||||
$(obj)/bootpz.o: $(obj)/kzsize.h
|
||||
|
||||
$(obj)/ksize.h: $(obj)/vmlinux.nh FORCE
|
||||
echo "#define KERNEL_SIZE `ls -l $(obj)/vmlinux.nh | awk '{print $$5}'`" > $@T
|
||||
ifdef INITRD
|
||||
[ -f $(INITRD) ] || exit 1
|
||||
echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T
|
||||
endif
|
||||
cmp -s $@T $@ || mv -f $@T $@
|
||||
rm -f $@T
|
||||
|
||||
$(obj)/kzsize.h: $(obj)/vmlinux.nh.gz FORCE
|
||||
echo "#define KERNEL_SIZE `ls -l $(obj)/vmlinux.nh | awk '{print $$5}'`" > $@T
|
||||
echo "#define KERNEL_Z_SIZE `ls -l $(obj)/vmlinux.nh.gz | awk '{print $$5}'`" >> $@T
|
||||
ifdef INITRD
|
||||
[ -f $(INITRD) ] || exit 1
|
||||
echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T
|
||||
endif
|
||||
cmp -s $@T $@ || mv -f $@T $@
|
||||
rm -f $@T
|
||||
|
||||
quiet_cmd_strip = STRIP $@
|
||||
cmd_strip = $(STRIP) -o $@ $<
|
||||
|
||||
$(obj)/vmlinux: vmlinux FORCE
|
||||
$(call if_changed,strip)
|
||||
|
||||
quiet_cmd_objstrip = OBJSTRIP $@
|
||||
cmd_objstrip = $(OBJSTRIP) $(OSFLAGS_$(@F)) $< $@
|
||||
|
||||
OSFLAGS_vmlinux.nh := -v
|
||||
OSFLAGS_lxboot := -p
|
||||
OSFLAGS_bootlx := -vb
|
||||
OSFLAGS_bootph := -vb
|
||||
OSFLAGS_bootpzh := -vb
|
||||
|
||||
$(obj)/vmlinux.nh: vmlinux $(OBJSTRIP) FORCE
|
||||
$(call if_changed,objstrip)
|
||||
|
||||
$(obj)/vmlinux.nh.gz: $(obj)/vmlinux.nh FORCE
|
||||
$(call if_changed,gzip)
|
||||
|
||||
$(obj)/tools/lxboot: $(obj)/bootloader $(OBJSTRIP) FORCE
|
||||
$(call if_changed,objstrip)
|
||||
|
||||
$(obj)/tools/bootlx: $(obj)/bootloader $(OBJSTRIP) FORCE
|
||||
$(call if_changed,objstrip)
|
||||
|
||||
$(obj)/tools/bootph: $(obj)/bootpheader $(OBJSTRIP) FORCE
|
||||
$(call if_changed,objstrip)
|
||||
|
||||
$(obj)/tools/bootpzh: $(obj)/bootpzheader $(OBJSTRIP) FORCE
|
||||
$(call if_changed,objstrip)
|
||||
|
||||
LDFLAGS_bootloader := -static -uvsprintf -T #-N -relax
|
||||
LDFLAGS_bootpheader := -static -uvsprintf -T #-N -relax
|
||||
LDFLAGS_bootpzheader := -static -uvsprintf -T #-N -relax
|
||||
|
||||
OBJ_bootlx := $(obj)/head.o $(obj)/main.o
|
||||
OBJ_bootph := $(obj)/head.o $(obj)/bootp.o
|
||||
OBJ_bootpzh := $(obj)/head.o $(obj)/bootpz.o $(obj)/misc.o
|
||||
|
||||
$(obj)/bootloader: $(obj)/bootloader.lds $(OBJ_bootlx) $(LIBS_Y) FORCE
|
||||
$(call if_changed,ld)
|
||||
|
||||
$(obj)/bootpheader: $(obj)/bootloader.lds $(OBJ_bootph) $(LIBS_Y) FORCE
|
||||
$(call if_changed,ld)
|
||||
|
||||
$(obj)/bootpzheader: $(obj)/bootloader.lds $(OBJ_bootpzh) $(LIBS_Y) FORCE
|
||||
$(call if_changed,ld)
|
||||
|
||||
$(obj)/misc.o: lib/inflate.c
|
24
arch/alpha/boot/bootloader.lds
Normal file
24
arch/alpha/boot/bootloader.lds
Normal file
|
@ -0,0 +1,24 @@
|
|||
OUTPUT_FORMAT("elf64-alpha")
|
||||
ENTRY(__start)
|
||||
printk = srm_printk;
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x20000000;
|
||||
.text : { *(.text) }
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.rodata : { *(.rodata) *(.rodata.*) }
|
||||
.data : { *(.data) CONSTRUCTORS }
|
||||
.got : { *(.got) }
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss : { *(.bss) *(COMMON) }
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
|
||||
.mdebug 0 : { *(.mdebug) }
|
||||
.note 0 : { *(.note) }
|
||||
.comment 0 : { *(.comment) }
|
||||
}
|
214
arch/alpha/boot/bootp.c
Normal file
214
arch/alpha/boot/bootp.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* arch/alpha/boot/bootp.c
|
||||
*
|
||||
* Copyright (C) 1997 Jay Estabrook
|
||||
*
|
||||
* This file is used for creating a bootp file for the Linux/AXP kernel
|
||||
*
|
||||
* based significantly on the arch/alpha/boot/main.c of Linus Torvalds
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <generated/utsrelease.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/console.h>
|
||||
#include <asm/hwrpb.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "ksize.h"
|
||||
|
||||
extern unsigned long switch_to_osf_pal(unsigned long nr,
|
||||
struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
|
||||
unsigned long *vptb);
|
||||
|
||||
extern void move_stack(unsigned long new_stack);
|
||||
|
||||
struct hwrpb_struct *hwrpb = INIT_HWRPB;
|
||||
static struct pcb_struct pcb_va[1];
|
||||
|
||||
/*
|
||||
* Find a physical address of a virtual object..
|
||||
*
|
||||
* This is easy using the virtual page table address.
|
||||
*/
|
||||
|
||||
static inline void *
|
||||
find_pa(unsigned long *vptb, void *ptr)
|
||||
{
|
||||
unsigned long address = (unsigned long) ptr;
|
||||
unsigned long result;
|
||||
|
||||
result = vptb[address >> 13];
|
||||
result >>= 32;
|
||||
result <<= 13;
|
||||
result |= address & 0x1fff;
|
||||
return (void *) result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function moves into OSF/1 pal-code, and has a temporary
|
||||
* PCB for that. The kernel proper should replace this PCB with
|
||||
* the real one as soon as possible.
|
||||
*
|
||||
* The page table muckery in here depends on the fact that the boot
|
||||
* code has the L1 page table identity-map itself in the second PTE
|
||||
* in the L1 page table. Thus the L1-page is virtually addressable
|
||||
* itself (through three levels) at virtual address 0x200802000.
|
||||
*/
|
||||
|
||||
#define VPTB ((unsigned long *) 0x200000000)
|
||||
#define L1 ((unsigned long *) 0x200802000)
|
||||
|
||||
void
|
||||
pal_init(void)
|
||||
{
|
||||
unsigned long i, rev;
|
||||
struct percpu_struct * percpu;
|
||||
struct pcb_struct * pcb_pa;
|
||||
|
||||
/* Create the dummy PCB. */
|
||||
pcb_va->ksp = 0;
|
||||
pcb_va->usp = 0;
|
||||
pcb_va->ptbr = L1[1] >> 32;
|
||||
pcb_va->asn = 0;
|
||||
pcb_va->pcc = 0;
|
||||
pcb_va->unique = 0;
|
||||
pcb_va->flags = 1;
|
||||
pcb_va->res1 = 0;
|
||||
pcb_va->res2 = 0;
|
||||
pcb_pa = find_pa(VPTB, pcb_va);
|
||||
|
||||
/*
|
||||
* a0 = 2 (OSF)
|
||||
* a1 = return address, but we give the asm the vaddr of the PCB
|
||||
* a2 = physical addr of PCB
|
||||
* a3 = new virtual page table pointer
|
||||
* a4 = KSP (but the asm sets it)
|
||||
*/
|
||||
srm_printk("Switching to OSF PAL-code .. ");
|
||||
|
||||
i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB);
|
||||
if (i) {
|
||||
srm_printk("failed, code %ld\n", i);
|
||||
__halt();
|
||||
}
|
||||
|
||||
percpu = (struct percpu_struct *)
|
||||
(INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB);
|
||||
rev = percpu->pal_revision = percpu->palcode_avail[2];
|
||||
|
||||
srm_printk("Ok (rev %lx)\n", rev);
|
||||
|
||||
tbia(); /* do it directly in case we are SMP */
|
||||
}
|
||||
|
||||
static inline void
|
||||
load(unsigned long dst, unsigned long src, unsigned long count)
|
||||
{
|
||||
memcpy((void *)dst, (void *)src, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the kernel.
|
||||
*/
|
||||
static inline void
|
||||
runkernel(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"bis %0,%0,$27\n\t"
|
||||
"jmp ($27)"
|
||||
: /* no outputs: it doesn't even return */
|
||||
: "r" (START_ADDR));
|
||||
}
|
||||
|
||||
extern char _end;
|
||||
#define KERNEL_ORIGIN \
|
||||
((((unsigned long)&_end) + 511) & ~511)
|
||||
|
||||
void
|
||||
start_kernel(void)
|
||||
{
|
||||
/*
|
||||
* Note that this crufty stuff with static and envval
|
||||
* and envbuf is because:
|
||||
*
|
||||
* 1. Frequently, the stack is short, and we don't want to overrun;
|
||||
* 2. Frequently the stack is where we are going to copy the kernel to;
|
||||
* 3. A certain SRM console required the GET_ENV output to stack.
|
||||
* ??? A comment in the aboot sources indicates that the GET_ENV
|
||||
* destination must be quadword aligned. Might this explain the
|
||||
* behaviour, rather than requiring output to the stack, which
|
||||
* seems rather far-fetched.
|
||||
*/
|
||||
static long nbytes;
|
||||
static char envval[256] __attribute__((aligned(8)));
|
||||
static unsigned long initrd_start;
|
||||
|
||||
srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n");
|
||||
if (INIT_HWRPB->pagesize != 8192) {
|
||||
srm_printk("Expected 8kB pages, got %ldkB\n",
|
||||
INIT_HWRPB->pagesize >> 10);
|
||||
return;
|
||||
}
|
||||
if (INIT_HWRPB->vptb != (unsigned long) VPTB) {
|
||||
srm_printk("Expected vptb at %p, got %p\n",
|
||||
VPTB, (void *)INIT_HWRPB->vptb);
|
||||
return;
|
||||
}
|
||||
pal_init();
|
||||
|
||||
/* The initrd must be page-aligned. See below for the
|
||||
cause of the magic number 5. */
|
||||
initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) |
|
||||
(PAGE_SIZE-1)) + 1;
|
||||
#ifdef INITRD_IMAGE_SIZE
|
||||
srm_printk("Initrd positioned at %#lx\n", initrd_start);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Move the stack to a safe place to ensure it won't be
|
||||
* overwritten by kernel image.
|
||||
*/
|
||||
move_stack(initrd_start - PAGE_SIZE);
|
||||
|
||||
nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval));
|
||||
if (nbytes < 0 || nbytes >= sizeof(envval)) {
|
||||
nbytes = 0;
|
||||
}
|
||||
envval[nbytes] = '\0';
|
||||
srm_printk("Loading the kernel...'%s'\n", envval);
|
||||
|
||||
/* NOTE: *no* callbacks or printouts from here on out!!! */
|
||||
|
||||
/* This is a hack, as some consoles seem to get virtual 20000000 (ie
|
||||
* where the SRM console puts the kernel bootp image) memory
|
||||
* overlapping physical memory where the kernel wants to be put,
|
||||
* which causes real problems when attempting to copy the former to
|
||||
* the latter... :-(
|
||||
*
|
||||
* So, we first move the kernel virtual-to-physical way above where
|
||||
* we physically want the kernel to end up, then copy it from there
|
||||
* to its final resting place... ;-}
|
||||
*
|
||||
* Sigh... */
|
||||
|
||||
#ifdef INITRD_IMAGE_SIZE
|
||||
load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE);
|
||||
#endif
|
||||
load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE);
|
||||
load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE);
|
||||
|
||||
memset((char*)ZERO_PGE, 0, PAGE_SIZE);
|
||||
strcpy((char*)ZERO_PGE, envval);
|
||||
#ifdef INITRD_IMAGE_SIZE
|
||||
((long *)(ZERO_PGE+256))[0] = initrd_start;
|
||||
((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE;
|
||||
#endif
|
||||
|
||||
runkernel();
|
||||
}
|
475
arch/alpha/boot/bootpz.c
Normal file
475
arch/alpha/boot/bootpz.c
Normal file
|
@ -0,0 +1,475 @@
|
|||
/*
|
||||
* arch/alpha/boot/bootpz.c
|
||||
*
|
||||
* Copyright (C) 1997 Jay Estabrook
|
||||
*
|
||||
* This file is used for creating a compressed BOOTP file for the
|
||||
* Linux/AXP kernel
|
||||
*
|
||||
* based significantly on the arch/alpha/boot/main.c of Linus Torvalds
|
||||
* and the decompression code from MILO.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <generated/utsrelease.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/console.h>
|
||||
#include <asm/hwrpb.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "kzsize.h"
|
||||
|
||||
/* FIXME FIXME FIXME */
|
||||
#define MALLOC_AREA_SIZE 0x200000 /* 2MB for now */
|
||||
/* FIXME FIXME FIXME */
|
||||
|
||||
|
||||
/*
|
||||
WARNING NOTE
|
||||
|
||||
It is very possible that turning on additional messages may cause
|
||||
kernel image corruption due to stack usage to do the printing.
|
||||
|
||||
*/
|
||||
|
||||
#undef DEBUG_CHECK_RANGE
|
||||
#undef DEBUG_ADDRESSES
|
||||
#undef DEBUG_LAST_STEPS
|
||||
|
||||
extern unsigned long switch_to_osf_pal(unsigned long nr,
|
||||
struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
|
||||
unsigned long *vptb);
|
||||
|
||||
extern int decompress_kernel(void* destination, void *source,
|
||||
size_t ksize, size_t kzsize);
|
||||
|
||||
extern void move_stack(unsigned long new_stack);
|
||||
|
||||
struct hwrpb_struct *hwrpb = INIT_HWRPB;
|
||||
static struct pcb_struct pcb_va[1];
|
||||
|
||||
/*
|
||||
* Find a physical address of a virtual object..
|
||||
*
|
||||
* This is easy using the virtual page table address.
|
||||
*/
|
||||
#define VPTB ((unsigned long *) 0x200000000)
|
||||
|
||||
static inline unsigned long
|
||||
find_pa(unsigned long address)
|
||||
{
|
||||
unsigned long result;
|
||||
|
||||
result = VPTB[address >> 13];
|
||||
result >>= 32;
|
||||
result <<= 13;
|
||||
result |= address & 0x1fff;
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
check_range(unsigned long vstart, unsigned long vend,
|
||||
unsigned long kstart, unsigned long kend)
|
||||
{
|
||||
unsigned long vaddr, kaddr;
|
||||
|
||||
#ifdef DEBUG_CHECK_RANGE
|
||||
srm_printk("check_range: V[0x%lx:0x%lx] K[0x%lx:0x%lx]\n",
|
||||
vstart, vend, kstart, kend);
|
||||
#endif
|
||||
/* do some range checking for detecting an overlap... */
|
||||
for (vaddr = vstart; vaddr <= vend; vaddr += PAGE_SIZE)
|
||||
{
|
||||
kaddr = (find_pa(vaddr) | PAGE_OFFSET);
|
||||
if (kaddr >= kstart && kaddr <= kend)
|
||||
{
|
||||
#ifdef DEBUG_CHECK_RANGE
|
||||
srm_printk("OVERLAP: vaddr 0x%lx kaddr 0x%lx"
|
||||
" [0x%lx:0x%lx]\n",
|
||||
vaddr, kaddr, kstart, kend);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function moves into OSF/1 pal-code, and has a temporary
|
||||
* PCB for that. The kernel proper should replace this PCB with
|
||||
* the real one as soon as possible.
|
||||
*
|
||||
* The page table muckery in here depends on the fact that the boot
|
||||
* code has the L1 page table identity-map itself in the second PTE
|
||||
* in the L1 page table. Thus the L1-page is virtually addressable
|
||||
* itself (through three levels) at virtual address 0x200802000.
|
||||
*/
|
||||
|
||||
#define L1 ((unsigned long *) 0x200802000)
|
||||
|
||||
void
|
||||
pal_init(void)
|
||||
{
|
||||
unsigned long i, rev;
|
||||
struct percpu_struct * percpu;
|
||||
struct pcb_struct * pcb_pa;
|
||||
|
||||
/* Create the dummy PCB. */
|
||||
pcb_va->ksp = 0;
|
||||
pcb_va->usp = 0;
|
||||
pcb_va->ptbr = L1[1] >> 32;
|
||||
pcb_va->asn = 0;
|
||||
pcb_va->pcc = 0;
|
||||
pcb_va->unique = 0;
|
||||
pcb_va->flags = 1;
|
||||
pcb_va->res1 = 0;
|
||||
pcb_va->res2 = 0;
|
||||
pcb_pa = (struct pcb_struct *)find_pa((unsigned long)pcb_va);
|
||||
|
||||
/*
|
||||
* a0 = 2 (OSF)
|
||||
* a1 = return address, but we give the asm the vaddr of the PCB
|
||||
* a2 = physical addr of PCB
|
||||
* a3 = new virtual page table pointer
|
||||
* a4 = KSP (but the asm sets it)
|
||||
*/
|
||||
srm_printk("Switching to OSF PAL-code... ");
|
||||
|
||||
i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB);
|
||||
if (i) {
|
||||
srm_printk("failed, code %ld\n", i);
|
||||
__halt();
|
||||
}
|
||||
|
||||
percpu = (struct percpu_struct *)
|
||||
(INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB);
|
||||
rev = percpu->pal_revision = percpu->palcode_avail[2];
|
||||
|
||||
srm_printk("OK (rev %lx)\n", rev);
|
||||
|
||||
tbia(); /* do it directly in case we are SMP */
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the kernel.
|
||||
*/
|
||||
static inline void
|
||||
runkernel(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"bis %0,%0,$27\n\t"
|
||||
"jmp ($27)"
|
||||
: /* no outputs: it doesn't even return */
|
||||
: "r" (START_ADDR));
|
||||
}
|
||||
|
||||
/* Must record the SP (it is virtual) on entry, so we can make sure
|
||||
not to overwrite it during movement or decompression. */
|
||||
unsigned long SP_on_entry;
|
||||
|
||||
/* Calculate the kernel image address based on the end of the BOOTP
|
||||
bootstrapper (ie this program).
|
||||
*/
|
||||
extern char _end;
|
||||
#define KERNEL_ORIGIN \
|
||||
((((unsigned long)&_end) + 511) & ~511)
|
||||
|
||||
/* Round address to next higher page boundary. */
|
||||
#define NEXT_PAGE(a) (((a) | (PAGE_SIZE - 1)) + 1)
|
||||
|
||||
#ifdef INITRD_IMAGE_SIZE
|
||||
# define REAL_INITRD_SIZE INITRD_IMAGE_SIZE
|
||||
#else
|
||||
# define REAL_INITRD_SIZE 0
|
||||
#endif
|
||||
|
||||
/* Defines from include/asm-alpha/system.h
|
||||
|
||||
BOOT_ADDR Virtual address at which the consoles loads
|
||||
the BOOTP image.
|
||||
|
||||
KERNEL_START KSEG address at which the kernel is built to run,
|
||||
which includes some initial data pages before the
|
||||
code.
|
||||
|
||||
START_ADDR KSEG address of the entry point of kernel code.
|
||||
|
||||
ZERO_PGE KSEG address of page full of zeroes, but
|
||||
upon entry to kerne cvan be expected
|
||||
to hold the parameter list and possible
|
||||
INTRD information.
|
||||
|
||||
These are used in the local defines below.
|
||||
*/
|
||||
|
||||
|
||||
/* Virtual addresses for the BOOTP image. Note that this includes the
|
||||
bootstrapper code as well as the compressed kernel image, and
|
||||
possibly the INITRD image.
|
||||
|
||||
Oh, and do NOT forget the STACK, which appears to be placed virtually
|
||||
beyond the end of the loaded image.
|
||||
*/
|
||||
#define V_BOOT_IMAGE_START BOOT_ADDR
|
||||
#define V_BOOT_IMAGE_END SP_on_entry
|
||||
|
||||
/* Virtual addresses for just the bootstrapper part of the BOOTP image. */
|
||||
#define V_BOOTSTRAPPER_START BOOT_ADDR
|
||||
#define V_BOOTSTRAPPER_END KERNEL_ORIGIN
|
||||
|
||||
/* Virtual addresses for just the data part of the BOOTP
|
||||
image. This may also include the INITRD image, but always
|
||||
includes the STACK.
|
||||
*/
|
||||
#define V_DATA_START KERNEL_ORIGIN
|
||||
#define V_INITRD_START (KERNEL_ORIGIN + KERNEL_Z_SIZE)
|
||||
#define V_INTRD_END (V_INITRD_START + REAL_INITRD_SIZE)
|
||||
#define V_DATA_END V_BOOT_IMAGE_END
|
||||
|
||||
/* KSEG addresses for the uncompressed kernel.
|
||||
|
||||
Note that the end address includes workspace for the decompression.
|
||||
Note also that the DATA_START address is ZERO_PGE, to which we write
|
||||
just before jumping to the kernel image at START_ADDR.
|
||||
*/
|
||||
#define K_KERNEL_DATA_START ZERO_PGE
|
||||
#define K_KERNEL_IMAGE_START START_ADDR
|
||||
#define K_KERNEL_IMAGE_END (START_ADDR + KERNEL_SIZE)
|
||||
|
||||
/* Define to where we may have to decompress the kernel image, before
|
||||
we move it to the final position, in case of overlap. This will be
|
||||
above the final position of the kernel.
|
||||
|
||||
Regardless of overlap, we move the INITRD image to the end of this
|
||||
copy area, because there needs to be a buffer area after the kernel
|
||||
for "bootmem" anyway.
|
||||
*/
|
||||
#define K_COPY_IMAGE_START NEXT_PAGE(K_KERNEL_IMAGE_END)
|
||||
/* Reserve one page below INITRD for the new stack. */
|
||||
#define K_INITRD_START \
|
||||
NEXT_PAGE(K_COPY_IMAGE_START + KERNEL_SIZE + PAGE_SIZE)
|
||||
#define K_COPY_IMAGE_END \
|
||||
(K_INITRD_START + REAL_INITRD_SIZE + MALLOC_AREA_SIZE)
|
||||
#define K_COPY_IMAGE_SIZE \
|
||||
NEXT_PAGE(K_COPY_IMAGE_END - K_COPY_IMAGE_START)
|
||||
|
||||
void
|
||||
start_kernel(void)
|
||||
{
|
||||
int must_move = 0;
|
||||
|
||||
/* Initialize these for the decompression-in-place situation,
|
||||
which is the smallest amount of work and most likely to
|
||||
occur when using the normal START_ADDR of the kernel
|
||||
(currently set to 16MB, to clear all console code.
|
||||
*/
|
||||
unsigned long uncompressed_image_start = K_KERNEL_IMAGE_START;
|
||||
unsigned long uncompressed_image_end = K_KERNEL_IMAGE_END;
|
||||
|
||||
unsigned long initrd_image_start = K_INITRD_START;
|
||||
|
||||
/*
|
||||
* Note that this crufty stuff with static and envval
|
||||
* and envbuf is because:
|
||||
*
|
||||
* 1. Frequently, the stack is short, and we don't want to overrun;
|
||||
* 2. Frequently the stack is where we are going to copy the kernel to;
|
||||
* 3. A certain SRM console required the GET_ENV output to stack.
|
||||
* ??? A comment in the aboot sources indicates that the GET_ENV
|
||||
* destination must be quadword aligned. Might this explain the
|
||||
* behaviour, rather than requiring output to the stack, which
|
||||
* seems rather far-fetched.
|
||||
*/
|
||||
static long nbytes;
|
||||
static char envval[256] __attribute__((aligned(8)));
|
||||
register unsigned long asm_sp asm("30");
|
||||
|
||||
SP_on_entry = asm_sp;
|
||||
|
||||
srm_printk("Linux/Alpha BOOTPZ Loader for Linux " UTS_RELEASE "\n");
|
||||
|
||||
/* Validity check the HWRPB. */
|
||||
if (INIT_HWRPB->pagesize != 8192) {
|
||||
srm_printk("Expected 8kB pages, got %ldkB\n",
|
||||
INIT_HWRPB->pagesize >> 10);
|
||||
return;
|
||||
}
|
||||
if (INIT_HWRPB->vptb != (unsigned long) VPTB) {
|
||||
srm_printk("Expected vptb at %p, got %p\n",
|
||||
VPTB, (void *)INIT_HWRPB->vptb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* PALcode (re)initialization. */
|
||||
pal_init();
|
||||
|
||||
/* Get the parameter list from the console environment variable. */
|
||||
nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval));
|
||||
if (nbytes < 0 || nbytes >= sizeof(envval)) {
|
||||
nbytes = 0;
|
||||
}
|
||||
envval[nbytes] = '\0';
|
||||
|
||||
#ifdef DEBUG_ADDRESSES
|
||||
srm_printk("START_ADDR 0x%lx\n", START_ADDR);
|
||||
srm_printk("KERNEL_ORIGIN 0x%lx\n", KERNEL_ORIGIN);
|
||||
srm_printk("KERNEL_SIZE 0x%x\n", KERNEL_SIZE);
|
||||
srm_printk("KERNEL_Z_SIZE 0x%x\n", KERNEL_Z_SIZE);
|
||||
#endif
|
||||
|
||||
/* Since all the SRM consoles load the BOOTP image at virtual
|
||||
* 0x20000000, we have to ensure that the physical memory
|
||||
* pages occupied by that image do NOT overlap the physical
|
||||
* address range where the kernel wants to be run. This
|
||||
* causes real problems when attempting to cdecompress the
|
||||
* former into the latter... :-(
|
||||
*
|
||||
* So, we may have to decompress/move the kernel/INITRD image
|
||||
* virtual-to-physical someplace else first before moving
|
||||
* kernel /INITRD to their final resting places... ;-}
|
||||
*
|
||||
* Sigh...
|
||||
*/
|
||||
|
||||
/* First, check to see if the range of addresses occupied by
|
||||
the bootstrapper part of the BOOTP image include any of the
|
||||
physical pages into which the kernel will be placed for
|
||||
execution.
|
||||
|
||||
We only need check on the final kernel image range, since we
|
||||
will put the INITRD someplace that we can be sure is not
|
||||
in conflict.
|
||||
*/
|
||||
if (check_range(V_BOOTSTRAPPER_START, V_BOOTSTRAPPER_END,
|
||||
K_KERNEL_DATA_START, K_KERNEL_IMAGE_END))
|
||||
{
|
||||
srm_printk("FATAL ERROR: overlap of bootstrapper code\n");
|
||||
__halt();
|
||||
}
|
||||
|
||||
/* Next, check to see if the range of addresses occupied by
|
||||
the compressed kernel/INITRD/stack portion of the BOOTP
|
||||
image include any of the physical pages into which the
|
||||
decompressed kernel or the INITRD will be placed for
|
||||
execution.
|
||||
*/
|
||||
if (check_range(V_DATA_START, V_DATA_END,
|
||||
K_KERNEL_IMAGE_START, K_COPY_IMAGE_END))
|
||||
{
|
||||
#ifdef DEBUG_ADDRESSES
|
||||
srm_printk("OVERLAP: cannot decompress in place\n");
|
||||
#endif
|
||||
uncompressed_image_start = K_COPY_IMAGE_START;
|
||||
uncompressed_image_end = K_COPY_IMAGE_END;
|
||||
must_move = 1;
|
||||
|
||||
/* Finally, check to see if the range of addresses
|
||||
occupied by the compressed kernel/INITRD part of
|
||||
the BOOTP image include any of the physical pages
|
||||
into which that part is to be copied for
|
||||
decompression.
|
||||
*/
|
||||
while (check_range(V_DATA_START, V_DATA_END,
|
||||
uncompressed_image_start,
|
||||
uncompressed_image_end))
|
||||
{
|
||||
#if 0
|
||||
uncompressed_image_start += K_COPY_IMAGE_SIZE;
|
||||
uncompressed_image_end += K_COPY_IMAGE_SIZE;
|
||||
initrd_image_start += K_COPY_IMAGE_SIZE;
|
||||
#else
|
||||
/* Keep as close as possible to end of BOOTP image. */
|
||||
uncompressed_image_start += PAGE_SIZE;
|
||||
uncompressed_image_end += PAGE_SIZE;
|
||||
initrd_image_start += PAGE_SIZE;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
srm_printk("Starting to load the kernel with args '%s'\n", envval);
|
||||
|
||||
#ifdef DEBUG_ADDRESSES
|
||||
srm_printk("Decompressing the kernel...\n"
|
||||
"...from 0x%lx to 0x%lx size 0x%x\n",
|
||||
V_DATA_START,
|
||||
uncompressed_image_start,
|
||||
KERNEL_SIZE);
|
||||
#endif
|
||||
decompress_kernel((void *)uncompressed_image_start,
|
||||
(void *)V_DATA_START,
|
||||
KERNEL_SIZE, KERNEL_Z_SIZE);
|
||||
|
||||
/*
|
||||
* Now, move things to their final positions, if/as required.
|
||||
*/
|
||||
|
||||
#ifdef INITRD_IMAGE_SIZE
|
||||
|
||||
/* First, we always move the INITRD image, if present. */
|
||||
#ifdef DEBUG_ADDRESSES
|
||||
srm_printk("Moving the INITRD image...\n"
|
||||
" from 0x%lx to 0x%lx size 0x%x\n",
|
||||
V_INITRD_START,
|
||||
initrd_image_start,
|
||||
INITRD_IMAGE_SIZE);
|
||||
#endif
|
||||
memcpy((void *)initrd_image_start, (void *)V_INITRD_START,
|
||||
INITRD_IMAGE_SIZE);
|
||||
|
||||
#endif /* INITRD_IMAGE_SIZE */
|
||||
|
||||
/* Next, we may have to move the uncompressed kernel to the
|
||||
final destination.
|
||||
*/
|
||||
if (must_move) {
|
||||
#ifdef DEBUG_ADDRESSES
|
||||
srm_printk("Moving the uncompressed kernel...\n"
|
||||
"...from 0x%lx to 0x%lx size 0x%x\n",
|
||||
uncompressed_image_start,
|
||||
K_KERNEL_IMAGE_START,
|
||||
(unsigned)KERNEL_SIZE);
|
||||
#endif
|
||||
/*
|
||||
* Move the stack to a safe place to ensure it won't be
|
||||
* overwritten by kernel image.
|
||||
*/
|
||||
move_stack(initrd_image_start - PAGE_SIZE);
|
||||
|
||||
memcpy((void *)K_KERNEL_IMAGE_START,
|
||||
(void *)uncompressed_image_start, KERNEL_SIZE);
|
||||
}
|
||||
|
||||
/* Clear the zero page, then move the argument list in. */
|
||||
#ifdef DEBUG_LAST_STEPS
|
||||
srm_printk("Preparing ZERO_PGE...\n");
|
||||
#endif
|
||||
memset((char*)ZERO_PGE, 0, PAGE_SIZE);
|
||||
strcpy((char*)ZERO_PGE, envval);
|
||||
|
||||
#ifdef INITRD_IMAGE_SIZE
|
||||
|
||||
#ifdef DEBUG_LAST_STEPS
|
||||
srm_printk("Preparing INITRD info...\n");
|
||||
#endif
|
||||
/* Finally, set the INITRD paramenters for the kernel. */
|
||||
((long *)(ZERO_PGE+256))[0] = initrd_image_start;
|
||||
((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE;
|
||||
|
||||
#endif /* INITRD_IMAGE_SIZE */
|
||||
|
||||
#ifdef DEBUG_LAST_STEPS
|
||||
srm_printk("Doing 'runkernel()'...\n");
|
||||
#endif
|
||||
runkernel();
|
||||
}
|
||||
|
||||
/* dummy function, should never be called. */
|
||||
void *__kmalloc(size_t size, gfp_t flags)
|
||||
{
|
||||
return (void *)NULL;
|
||||
}
|
123
arch/alpha/boot/head.S
Normal file
123
arch/alpha/boot/head.S
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* arch/alpha/boot/head.S
|
||||
*
|
||||
* initial bootloader stuff..
|
||||
*/
|
||||
|
||||
#include <asm/pal.h>
|
||||
|
||||
.set noreorder
|
||||
.globl __start
|
||||
.ent __start
|
||||
__start:
|
||||
br $29,2f
|
||||
2: ldgp $29,0($29)
|
||||
jsr $26,start_kernel
|
||||
call_pal PAL_halt
|
||||
.end __start
|
||||
|
||||
.align 5
|
||||
.globl wrent
|
||||
.ent wrent
|
||||
wrent:
|
||||
.prologue 0
|
||||
call_pal PAL_wrent
|
||||
ret ($26)
|
||||
.end wrent
|
||||
|
||||
.align 5
|
||||
.globl wrkgp
|
||||
.ent wrkgp
|
||||
wrkgp:
|
||||
.prologue 0
|
||||
call_pal PAL_wrkgp
|
||||
ret ($26)
|
||||
.end wrkgp
|
||||
|
||||
.align 5
|
||||
.globl switch_to_osf_pal
|
||||
.ent switch_to_osf_pal
|
||||
switch_to_osf_pal:
|
||||
subq $30,128,$30
|
||||
.frame $30,128,$26
|
||||
stq $26,0($30)
|
||||
stq $1,8($30)
|
||||
stq $2,16($30)
|
||||
stq $3,24($30)
|
||||
stq $4,32($30)
|
||||
stq $5,40($30)
|
||||
stq $6,48($30)
|
||||
stq $7,56($30)
|
||||
stq $8,64($30)
|
||||
stq $9,72($30)
|
||||
stq $10,80($30)
|
||||
stq $11,88($30)
|
||||
stq $12,96($30)
|
||||
stq $13,104($30)
|
||||
stq $14,112($30)
|
||||
stq $15,120($30)
|
||||
.prologue 0
|
||||
|
||||
stq $30,0($17) /* save KSP in PCB */
|
||||
|
||||
bis $30,$30,$20 /* a4 = KSP */
|
||||
br $17,1f
|
||||
|
||||
ldq $26,0($30)
|
||||
ldq $1,8($30)
|
||||
ldq $2,16($30)
|
||||
ldq $3,24($30)
|
||||
ldq $4,32($30)
|
||||
ldq $5,40($30)
|
||||
ldq $6,48($30)
|
||||
ldq $7,56($30)
|
||||
ldq $8,64($30)
|
||||
ldq $9,72($30)
|
||||
ldq $10,80($30)
|
||||
ldq $11,88($30)
|
||||
ldq $12,96($30)
|
||||
ldq $13,104($30)
|
||||
ldq $14,112($30)
|
||||
ldq $15,120($30)
|
||||
addq $30,128,$30
|
||||
ret ($26)
|
||||
1: call_pal PAL_swppal
|
||||
.end switch_to_osf_pal
|
||||
|
||||
.align 3
|
||||
.globl tbi
|
||||
.ent tbi
|
||||
tbi:
|
||||
.prologue 0
|
||||
call_pal PAL_tbi
|
||||
ret ($26)
|
||||
.end tbi
|
||||
|
||||
.align 3
|
||||
.globl halt
|
||||
.ent halt
|
||||
halt:
|
||||
.prologue 0
|
||||
call_pal PAL_halt
|
||||
.end halt
|
||||
|
||||
/* $16 - new stack page */
|
||||
.align 3
|
||||
.globl move_stack
|
||||
.ent move_stack
|
||||
move_stack:
|
||||
.prologue 0
|
||||
lda $0, 0x1fff($31)
|
||||
and $0, $30, $1 /* Stack offset */
|
||||
or $1, $16, $16 /* New stack pointer */
|
||||
mov $30, $1
|
||||
mov $16, $2
|
||||
1: ldq $3, 0($1) /* Move the stack */
|
||||
addq $1, 8, $1
|
||||
stq $3, 0($2)
|
||||
and $0, $1, $4
|
||||
addq $2, 8, $2
|
||||
bne $4, 1b
|
||||
mov $16, $30
|
||||
ret ($26)
|
||||
.end move_stack
|
191
arch/alpha/boot/main.c
Normal file
191
arch/alpha/boot/main.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* arch/alpha/boot/main.c
|
||||
*
|
||||
* Copyright (C) 1994, 1995 Linus Torvalds
|
||||
*
|
||||
* This file is the bootloader for the Linux/AXP kernel
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <generated/utsrelease.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/console.h>
|
||||
#include <asm/hwrpb.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "ksize.h"
|
||||
|
||||
extern int vsprintf(char *, const char *, va_list);
|
||||
extern unsigned long switch_to_osf_pal(unsigned long nr,
|
||||
struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
|
||||
unsigned long *vptb);
|
||||
struct hwrpb_struct *hwrpb = INIT_HWRPB;
|
||||
static struct pcb_struct pcb_va[1];
|
||||
|
||||
/*
|
||||
* Find a physical address of a virtual object..
|
||||
*
|
||||
* This is easy using the virtual page table address.
|
||||
*/
|
||||
|
||||
static inline void *
|
||||
find_pa(unsigned long *vptb, void *ptr)
|
||||
{
|
||||
unsigned long address = (unsigned long) ptr;
|
||||
unsigned long result;
|
||||
|
||||
result = vptb[address >> 13];
|
||||
result >>= 32;
|
||||
result <<= 13;
|
||||
result |= address & 0x1fff;
|
||||
return (void *) result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function moves into OSF/1 pal-code, and has a temporary
|
||||
* PCB for that. The kernel proper should replace this PCB with
|
||||
* the real one as soon as possible.
|
||||
*
|
||||
* The page table muckery in here depends on the fact that the boot
|
||||
* code has the L1 page table identity-map itself in the second PTE
|
||||
* in the L1 page table. Thus the L1-page is virtually addressable
|
||||
* itself (through three levels) at virtual address 0x200802000.
|
||||
*/
|
||||
|
||||
#define VPTB ((unsigned long *) 0x200000000)
|
||||
#define L1 ((unsigned long *) 0x200802000)
|
||||
|
||||
void
|
||||
pal_init(void)
|
||||
{
|
||||
unsigned long i, rev;
|
||||
struct percpu_struct * percpu;
|
||||
struct pcb_struct * pcb_pa;
|
||||
|
||||
/* Create the dummy PCB. */
|
||||
pcb_va->ksp = 0;
|
||||
pcb_va->usp = 0;
|
||||
pcb_va->ptbr = L1[1] >> 32;
|
||||
pcb_va->asn = 0;
|
||||
pcb_va->pcc = 0;
|
||||
pcb_va->unique = 0;
|
||||
pcb_va->flags = 1;
|
||||
pcb_va->res1 = 0;
|
||||
pcb_va->res2 = 0;
|
||||
pcb_pa = find_pa(VPTB, pcb_va);
|
||||
|
||||
/*
|
||||
* a0 = 2 (OSF)
|
||||
* a1 = return address, but we give the asm the vaddr of the PCB
|
||||
* a2 = physical addr of PCB
|
||||
* a3 = new virtual page table pointer
|
||||
* a4 = KSP (but the asm sets it)
|
||||
*/
|
||||
srm_printk("Switching to OSF PAL-code .. ");
|
||||
|
||||
i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB);
|
||||
if (i) {
|
||||
srm_printk("failed, code %ld\n", i);
|
||||
__halt();
|
||||
}
|
||||
|
||||
percpu = (struct percpu_struct *)
|
||||
(INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB);
|
||||
rev = percpu->pal_revision = percpu->palcode_avail[2];
|
||||
|
||||
srm_printk("Ok (rev %lx)\n", rev);
|
||||
|
||||
tbia(); /* do it directly in case we are SMP */
|
||||
}
|
||||
|
||||
static inline long openboot(void)
|
||||
{
|
||||
char bootdev[256];
|
||||
long result;
|
||||
|
||||
result = callback_getenv(ENV_BOOTED_DEV, bootdev, 255);
|
||||
if (result < 0)
|
||||
return result;
|
||||
return callback_open(bootdev, result & 255);
|
||||
}
|
||||
|
||||
static inline long close(long dev)
|
||||
{
|
||||
return callback_close(dev);
|
||||
}
|
||||
|
||||
static inline long load(long dev, unsigned long addr, unsigned long count)
|
||||
{
|
||||
char bootfile[256];
|
||||
extern char _end;
|
||||
long result, boot_size = &_end - (char *) BOOT_ADDR;
|
||||
|
||||
result = callback_getenv(ENV_BOOTED_FILE, bootfile, 255);
|
||||
if (result < 0)
|
||||
return result;
|
||||
result &= 255;
|
||||
bootfile[result] = '\0';
|
||||
if (result)
|
||||
srm_printk("Boot file specification (%s) not implemented\n",
|
||||
bootfile);
|
||||
return callback_read(dev, count, (void *)addr, boot_size/512 + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the kernel.
|
||||
*/
|
||||
static void runkernel(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"bis %1,%1,$30\n\t"
|
||||
"bis %0,%0,$26\n\t"
|
||||
"ret ($26)"
|
||||
: /* no outputs: it doesn't even return */
|
||||
: "r" (START_ADDR),
|
||||
"r" (PAGE_SIZE + INIT_STACK));
|
||||
}
|
||||
|
||||
void start_kernel(void)
|
||||
{
|
||||
long i;
|
||||
long dev;
|
||||
int nbytes;
|
||||
char envval[256];
|
||||
|
||||
srm_printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n");
|
||||
if (INIT_HWRPB->pagesize != 8192) {
|
||||
srm_printk("Expected 8kB pages, got %ldkB\n", INIT_HWRPB->pagesize >> 10);
|
||||
return;
|
||||
}
|
||||
pal_init();
|
||||
dev = openboot();
|
||||
if (dev < 0) {
|
||||
srm_printk("Unable to open boot device: %016lx\n", dev);
|
||||
return;
|
||||
}
|
||||
dev &= 0xffffffff;
|
||||
srm_printk("Loading vmlinux ...");
|
||||
i = load(dev, START_ADDR, KERNEL_SIZE);
|
||||
close(dev);
|
||||
if (i != KERNEL_SIZE) {
|
||||
srm_printk("Failed (%lx)\n", i);
|
||||
return;
|
||||
}
|
||||
|
||||
nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval));
|
||||
if (nbytes < 0) {
|
||||
nbytes = 0;
|
||||
}
|
||||
envval[nbytes] = '\0';
|
||||
strcpy((char*)ZERO_PGE, envval);
|
||||
|
||||
srm_printk(" Ok\nNow booting the kernel\n");
|
||||
runkernel();
|
||||
for (i = 0 ; i < 0x100000000 ; i++)
|
||||
/* nothing */;
|
||||
__halt();
|
||||
}
|
173
arch/alpha/boot/misc.c
Normal file
173
arch/alpha/boot/misc.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* misc.c
|
||||
*
|
||||
* This is a collection of several routines from gzip-1.0.3
|
||||
* adapted for Linux.
|
||||
*
|
||||
* malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
|
||||
*
|
||||
* Modified for ARM Linux by Russell King
|
||||
*
|
||||
* Nicolas Pitre <nico@visuaide.com> 1999/04/14 :
|
||||
* For this code to run directly from Flash, all constant variables must
|
||||
* be marked with 'const' and all other variables initialized at run-time
|
||||
* only. This way all non constant variables will end up in the bss segment,
|
||||
* which should point to addresses in RAM and cleared to 0 on start.
|
||||
* This allows for a much quicker boot time.
|
||||
*
|
||||
* Modified for Alpha, from the ARM version, by Jay Estabrook 2003.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define memzero(s,n) memset ((s),0,(n))
|
||||
#define puts srm_printk
|
||||
extern long srm_printk(const char *, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
/*
|
||||
* gzip delarations
|
||||
*/
|
||||
#define OF(args) args
|
||||
#define STATIC static
|
||||
|
||||
typedef unsigned char uch;
|
||||
typedef unsigned short ush;
|
||||
typedef unsigned long ulg;
|
||||
|
||||
#define WSIZE 0x8000 /* Window size must be at least 32k, */
|
||||
/* and a power of two */
|
||||
|
||||
static uch *inbuf; /* input buffer */
|
||||
static uch *window; /* Sliding window buffer */
|
||||
|
||||
static unsigned insize; /* valid bytes in inbuf */
|
||||
static unsigned inptr; /* index of next byte to be processed in inbuf */
|
||||
static unsigned outcnt; /* bytes in output buffer */
|
||||
|
||||
/* gzip flag byte */
|
||||
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
|
||||
#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
|
||||
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
|
||||
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
|
||||
#define COMMENT 0x10 /* bit 4 set: file comment present */
|
||||
#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
|
||||
#define RESERVED 0xC0 /* bit 6,7: reserved */
|
||||
|
||||
#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
|
||||
|
||||
/* Diagnostic functions */
|
||||
#ifdef DEBUG
|
||||
# define Assert(cond,msg) {if(!(cond)) error(msg);}
|
||||
# define Trace(x) fprintf x
|
||||
# define Tracev(x) {if (verbose) fprintf x ;}
|
||||
# define Tracevv(x) {if (verbose>1) fprintf x ;}
|
||||
# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
|
||||
# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
|
||||
#else
|
||||
# define Assert(cond,msg)
|
||||
# define Trace(x)
|
||||
# define Tracev(x)
|
||||
# define Tracevv(x)
|
||||
# define Tracec(c,x)
|
||||
# define Tracecv(c,x)
|
||||
#endif
|
||||
|
||||
static int fill_inbuf(void);
|
||||
static void flush_window(void);
|
||||
static void error(char *m);
|
||||
|
||||
static char *input_data;
|
||||
static int input_data_size;
|
||||
|
||||
static uch *output_data;
|
||||
static ulg output_ptr;
|
||||
static ulg bytes_out;
|
||||
|
||||
static void error(char *m);
|
||||
static void gzip_mark(void **);
|
||||
static void gzip_release(void **);
|
||||
|
||||
extern int end;
|
||||
static ulg free_mem_ptr;
|
||||
static ulg free_mem_end_ptr;
|
||||
|
||||
#define HEAP_SIZE 0x3000
|
||||
|
||||
#include "../../../lib/inflate.c"
|
||||
|
||||
/* ===========================================================================
|
||||
* Fill the input buffer. This is called only when the buffer is empty
|
||||
* and at least one byte is really needed.
|
||||
*/
|
||||
int fill_inbuf(void)
|
||||
{
|
||||
if (insize != 0)
|
||||
error("ran out of input data");
|
||||
|
||||
inbuf = input_data;
|
||||
insize = input_data_size;
|
||||
|
||||
inptr = 1;
|
||||
return inbuf[0];
|
||||
}
|
||||
|
||||
/* ===========================================================================
|
||||
* Write the output window window[0..outcnt-1] and update crc and bytes_out.
|
||||
* (Used for the decompressed data only.)
|
||||
*/
|
||||
void flush_window(void)
|
||||
{
|
||||
ulg c = crc;
|
||||
unsigned n;
|
||||
uch *in, *out, ch;
|
||||
|
||||
in = window;
|
||||
out = &output_data[output_ptr];
|
||||
for (n = 0; n < outcnt; n++) {
|
||||
ch = *out++ = *in++;
|
||||
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
|
||||
}
|
||||
crc = c;
|
||||
bytes_out += (ulg)outcnt;
|
||||
output_ptr += (ulg)outcnt;
|
||||
outcnt = 0;
|
||||
/* puts("."); */
|
||||
}
|
||||
|
||||
static void error(char *x)
|
||||
{
|
||||
puts("\n\n");
|
||||
puts(x);
|
||||
puts("\n\n -- System halted");
|
||||
|
||||
while(1); /* Halt */
|
||||
}
|
||||
|
||||
unsigned int
|
||||
decompress_kernel(void *output_start,
|
||||
void *input_start,
|
||||
size_t ksize,
|
||||
size_t kzsize)
|
||||
{
|
||||
output_data = (uch *)output_start;
|
||||
input_data = (uch *)input_start;
|
||||
input_data_size = kzsize; /* use compressed size */
|
||||
|
||||
/* FIXME FIXME FIXME */
|
||||
free_mem_ptr = (ulg)output_start + ksize;
|
||||
free_mem_end_ptr = (ulg)output_start + ksize + 0x200000;
|
||||
/* FIXME FIXME FIXME */
|
||||
|
||||
/* put in temp area to reduce initial footprint */
|
||||
window = malloc(WSIZE);
|
||||
|
||||
makecrc();
|
||||
/* puts("Uncompressing Linux..."); */
|
||||
gunzip();
|
||||
/* puts(" done, booting the kernel.\n"); */
|
||||
return output_ptr;
|
||||
}
|
152
arch/alpha/boot/tools/mkbb.c
Normal file
152
arch/alpha/boot/tools/mkbb.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
/* This utility makes a bootblock suitable for the SRM console/miniloader */
|
||||
|
||||
/* Usage:
|
||||
* mkbb <device> <lxboot>
|
||||
*
|
||||
* Where <device> is the name of the device to install the bootblock on,
|
||||
* and <lxboot> is the name of a bootblock to merge in. This bootblock
|
||||
* contains the offset and size of the bootloader. It must be exactly
|
||||
* 512 bytes long.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Minimal definition of disklabel, so we don't have to include
|
||||
* asm/disklabel.h (confuses make)
|
||||
*/
|
||||
#ifndef MAXPARTITIONS
|
||||
#define MAXPARTITIONS 8 /* max. # of partitions */
|
||||
#endif
|
||||
|
||||
#ifndef u8
|
||||
#define u8 unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef u16
|
||||
#define u16 unsigned short
|
||||
#endif
|
||||
|
||||
#ifndef u32
|
||||
#define u32 unsigned int
|
||||
#endif
|
||||
|
||||
struct disklabel {
|
||||
u32 d_magic; /* must be DISKLABELMAGIC */
|
||||
u16 d_type, d_subtype;
|
||||
u8 d_typename[16];
|
||||
u8 d_packname[16];
|
||||
u32 d_secsize;
|
||||
u32 d_nsectors;
|
||||
u32 d_ntracks;
|
||||
u32 d_ncylinders;
|
||||
u32 d_secpercyl;
|
||||
u32 d_secprtunit;
|
||||
u16 d_sparespertrack;
|
||||
u16 d_sparespercyl;
|
||||
u32 d_acylinders;
|
||||
u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
|
||||
u32 d_headswitch, d_trkseek, d_flags;
|
||||
u32 d_drivedata[5];
|
||||
u32 d_spare[5];
|
||||
u32 d_magic2; /* must be DISKLABELMAGIC */
|
||||
u16 d_checksum;
|
||||
u16 d_npartitions;
|
||||
u32 d_bbsize, d_sbsize;
|
||||
struct d_partition {
|
||||
u32 p_size;
|
||||
u32 p_offset;
|
||||
u32 p_fsize;
|
||||
u8 p_fstype;
|
||||
u8 p_frag;
|
||||
u16 p_cpg;
|
||||
} d_partitions[MAXPARTITIONS];
|
||||
};
|
||||
|
||||
|
||||
typedef union __bootblock {
|
||||
struct {
|
||||
char __pad1[64];
|
||||
struct disklabel __label;
|
||||
} __u1;
|
||||
struct {
|
||||
unsigned long __pad2[63];
|
||||
unsigned long __checksum;
|
||||
} __u2;
|
||||
char bootblock_bytes[512];
|
||||
unsigned long bootblock_quadwords[64];
|
||||
} bootblock;
|
||||
|
||||
#define bootblock_label __u1.__label
|
||||
#define bootblock_checksum __u2.__checksum
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
bootblock bootblock_from_disk;
|
||||
bootblock bootloader_image;
|
||||
int dev, fd;
|
||||
int i;
|
||||
int nread;
|
||||
|
||||
/* Make sure of the arg count */
|
||||
if(argc != 3) {
|
||||
fprintf(stderr, "Usage: %s device lxboot\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* First, open the device and make sure it's accessible */
|
||||
dev = open(argv[1], O_RDWR);
|
||||
if(dev < 0) {
|
||||
perror(argv[1]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Now open the lxboot and make sure it's reasonable */
|
||||
fd = open(argv[2], O_RDONLY);
|
||||
if(fd < 0) {
|
||||
perror(argv[2]);
|
||||
close(dev);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Read in the lxboot */
|
||||
nread = read(fd, &bootloader_image, sizeof(bootblock));
|
||||
if(nread != sizeof(bootblock)) {
|
||||
perror("lxboot read");
|
||||
fprintf(stderr, "expected %zd, got %d\n", sizeof(bootblock), nread);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Read in the bootblock from disk. */
|
||||
nread = read(dev, &bootblock_from_disk, sizeof(bootblock));
|
||||
if(nread != sizeof(bootblock)) {
|
||||
perror("bootblock read");
|
||||
fprintf(stderr, "expected %zd, got %d\n", sizeof(bootblock), nread);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Swap the bootblock's disklabel into the bootloader */
|
||||
bootloader_image.bootblock_label = bootblock_from_disk.bootblock_label;
|
||||
|
||||
/* Calculate the bootblock checksum */
|
||||
bootloader_image.bootblock_checksum = 0;
|
||||
for(i = 0; i < 63; i++) {
|
||||
bootloader_image.bootblock_checksum +=
|
||||
bootloader_image.bootblock_quadwords[i];
|
||||
}
|
||||
|
||||
/* Write the whole thing out! */
|
||||
lseek(dev, 0L, SEEK_SET);
|
||||
if(write(dev, &bootloader_image, sizeof(bootblock)) != sizeof(bootblock)) {
|
||||
perror("bootblock write");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
close(dev);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
280
arch/alpha/boot/tools/objstrip.c
Normal file
280
arch/alpha/boot/tools/objstrip.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* arch/alpha/boot/tools/objstrip.c
|
||||
*
|
||||
* Strip the object file headers/trailers from an executable (ELF or ECOFF).
|
||||
*
|
||||
* Copyright (C) 1996 David Mosberger-Tang.
|
||||
*/
|
||||
/*
|
||||
* Converts an ECOFF or ELF object file into a bootable file. The
|
||||
* object file must be a OMAGIC file (i.e., data and bss follow immediately
|
||||
* behind the text). See DEC "Assembly Language Programmer's Guide"
|
||||
* documentation for details. The SRM boot process is documented in
|
||||
* the Alpha AXP Architecture Reference Manual, Second Edition by
|
||||
* Richard L. Sites and Richard T. Witek.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/a.out.h>
|
||||
#include <linux/coff.h>
|
||||
#include <linux/param.h>
|
||||
#ifdef __ELF__
|
||||
# include <linux/elf.h>
|
||||
#endif
|
||||
|
||||
/* bootfile size must be multiple of BLOCK_SIZE: */
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
const char * prog_name;
|
||||
|
||||
|
||||
static void
|
||||
usage (void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [-v] -p file primary\n"
|
||||
" %s [-vb] file [secondary]\n", prog_name, prog_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
size_t nwritten, tocopy, n, mem_size, fil_size, pad = 0;
|
||||
int fd, ofd, i, j, verbose = 0, primary = 0;
|
||||
char buf[8192], *inname;
|
||||
struct exec * aout; /* includes file & aout header */
|
||||
long offset;
|
||||
#ifdef __ELF__
|
||||
struct elfhdr *elf;
|
||||
struct elf_phdr *elf_phdr; /* program header */
|
||||
unsigned long long e_entry;
|
||||
#endif
|
||||
|
||||
prog_name = argv[0];
|
||||
|
||||
for (i = 1; i < argc && argv[i][0] == '-'; ++i) {
|
||||
for (j = 1; argv[i][j]; ++j) {
|
||||
switch (argv[i][j]) {
|
||||
case 'v':
|
||||
verbose = ~verbose;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
pad = BLOCK_SIZE;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
primary = 1; /* make primary bootblock */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= argc) {
|
||||
usage();
|
||||
}
|
||||
inname = argv[i++];
|
||||
|
||||
fd = open(inname, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror("open");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ofd = 1;
|
||||
if (i < argc) {
|
||||
ofd = open(argv[i++], O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (ofd == -1) {
|
||||
perror("open");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (primary) {
|
||||
/* generate bootblock for primary loader */
|
||||
|
||||
unsigned long bb[64], sum = 0;
|
||||
struct stat st;
|
||||
off_t size;
|
||||
int i;
|
||||
|
||||
if (ofd == 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if (fstat(fd, &st) == -1) {
|
||||
perror("fstat");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size = (st.st_size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1);
|
||||
memset(bb, 0, sizeof(bb));
|
||||
strcpy((char *) bb, "Linux SRM bootblock");
|
||||
bb[60] = size / BLOCK_SIZE; /* count */
|
||||
bb[61] = 1; /* starting sector # */
|
||||
bb[62] = 0; /* flags---must be 0 */
|
||||
for (i = 0; i < 63; ++i) {
|
||||
sum += bb[i];
|
||||
}
|
||||
bb[63] = sum;
|
||||
if (write(ofd, bb, sizeof(bb)) != sizeof(bb)) {
|
||||
perror("boot-block write");
|
||||
exit(1);
|
||||
}
|
||||
printf("%lu\n", size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read and inspect exec header: */
|
||||
|
||||
if (read(fd, buf, sizeof(buf)) < 0) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef __ELF__
|
||||
elf = (struct elfhdr *) buf;
|
||||
|
||||
if (elf->e_ident[0] == 0x7f && strncmp((char *)elf->e_ident + 1, "ELF", 3) == 0) {
|
||||
if (elf->e_type != ET_EXEC) {
|
||||
fprintf(stderr, "%s: %s is not an ELF executable\n",
|
||||
prog_name, inname);
|
||||
exit(1);
|
||||
}
|
||||
if (!elf_check_arch(elf)) {
|
||||
fprintf(stderr, "%s: is not for this processor (e_machine=%d)\n",
|
||||
prog_name, elf->e_machine);
|
||||
exit(1);
|
||||
}
|
||||
if (elf->e_phnum != 1) {
|
||||
fprintf(stderr,
|
||||
"%s: %d program headers (forgot to link with -N?)\n",
|
||||
prog_name, elf->e_phnum);
|
||||
}
|
||||
|
||||
e_entry = elf->e_entry;
|
||||
|
||||
lseek(fd, elf->e_phoff, SEEK_SET);
|
||||
if (read(fd, buf, sizeof(*elf_phdr)) != sizeof(*elf_phdr)) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
elf_phdr = (struct elf_phdr *) buf;
|
||||
offset = elf_phdr->p_offset;
|
||||
mem_size = elf_phdr->p_memsz;
|
||||
fil_size = elf_phdr->p_filesz;
|
||||
|
||||
/* work around ELF bug: */
|
||||
if (elf_phdr->p_vaddr < e_entry) {
|
||||
unsigned long delta = e_entry - elf_phdr->p_vaddr;
|
||||
offset += delta;
|
||||
mem_size -= delta;
|
||||
fil_size -= delta;
|
||||
elf_phdr->p_vaddr += delta;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n",
|
||||
prog_name, (long) elf_phdr->p_vaddr,
|
||||
elf_phdr->p_vaddr + fil_size, offset);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
aout = (struct exec *) buf;
|
||||
|
||||
if (!(aout->fh.f_flags & COFF_F_EXEC)) {
|
||||
fprintf(stderr, "%s: %s is not in executable format\n",
|
||||
prog_name, inname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (aout->fh.f_opthdr != sizeof(aout->ah)) {
|
||||
fprintf(stderr, "%s: %s has unexpected optional header size\n",
|
||||
prog_name, inname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (N_MAGIC(*aout) != OMAGIC) {
|
||||
fprintf(stderr, "%s: %s is not an OMAGIC file\n",
|
||||
prog_name, inname);
|
||||
exit(1);
|
||||
}
|
||||
offset = N_TXTOFF(*aout);
|
||||
fil_size = aout->ah.tsize + aout->ah.dsize;
|
||||
mem_size = fil_size + aout->ah.bsize;
|
||||
|
||||
if (verbose) {
|
||||
fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n",
|
||||
prog_name, aout->ah.text_start,
|
||||
aout->ah.text_start + fil_size, offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
perror("lseek");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
fprintf(stderr, "%s: copying %lu byte from %s\n",
|
||||
prog_name, (unsigned long) fil_size, inname);
|
||||
}
|
||||
|
||||
tocopy = fil_size;
|
||||
while (tocopy > 0) {
|
||||
n = tocopy;
|
||||
if (n > sizeof(buf)) {
|
||||
n = sizeof(buf);
|
||||
}
|
||||
tocopy -= n;
|
||||
if ((size_t) read(fd, buf, n) != n) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
do {
|
||||
nwritten = write(ofd, buf, n);
|
||||
if ((ssize_t) nwritten == -1) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
n -= nwritten;
|
||||
} while (n > 0);
|
||||
}
|
||||
|
||||
if (pad) {
|
||||
mem_size = ((mem_size + pad - 1) / pad) * pad;
|
||||
}
|
||||
|
||||
tocopy = mem_size - fil_size;
|
||||
if (tocopy > 0) {
|
||||
fprintf(stderr,
|
||||
"%s: zero-filling bss and aligning to %lu with %lu bytes\n",
|
||||
prog_name, pad, (unsigned long) tocopy);
|
||||
|
||||
memset(buf, 0x00, sizeof(buf));
|
||||
do {
|
||||
n = tocopy;
|
||||
if (n > sizeof(buf)) {
|
||||
n = sizeof(buf);
|
||||
}
|
||||
nwritten = write(ofd, buf, n);
|
||||
if ((ssize_t) nwritten == -1) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
tocopy -= nwritten;
|
||||
} while (tocopy > 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue