[Armadeus-commitlog] armadeus branch, master, updated. armadeus-7.0-367-g83c25cc88
Brought to you by:
sszy
|
From: sszy <ss...@us...> - 2023-02-24 08:47:51
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "armadeus".
The branch, master has been updated
via 83c25cc881d0c35b5bf74b2c5f2f5d6f391e6667 (commit)
via 30d9874bbbe5680797f020e8db1641e57b86444a (commit)
from 1e7376970e217d9fbb3ef86417facaf903ffe287 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 83c25cc881d0c35b5bf74b2c5f2f5d6f391e6667
Author: Sébastien Szymanski <seb...@ar...>
Date: Thu Feb 23 17:10:44 2023 +0100
[TOOLS] apf27_recovery.py: python3 support and cleanup
commit 30d9874bbbe5680797f020e8db1641e57b86444a
Author: Sébastien Szymanski <seb...@ar...>
Date: Thu Feb 23 10:00:21 2023 +0100
[BUILDROOT] 2022.02: patches to fixed rt-tests downloading
* Bump rt-tests to version 2.5
-----------------------------------------------------------------------
Summary of changes:
...419-package-rt-tests-bump-version-to-2.5.patch} | 20 +-
software/uboot_recover/apf27_recover.py | 329 ++++++++-------------
2 files changed, 131 insertions(+), 218 deletions(-)
copy patches/buildroot/2022.02/{415-package-rt-tests-bump-version-to-2.4.patch => 419-package-rt-tests-bump-version-to-2.5.patch} (71%)
diff --git a/patches/buildroot/2022.02/415-package-rt-tests-bump-version-to-2.4.patch b/patches/buildroot/2022.02/419-package-rt-tests-bump-version-to-2.5.patch
similarity index 71%
copy from patches/buildroot/2022.02/415-package-rt-tests-bump-version-to-2.4.patch
copy to patches/buildroot/2022.02/419-package-rt-tests-bump-version-to-2.5.patch
index c7288580c..c1d980751 100644
--- a/patches/buildroot/2022.02/415-package-rt-tests-bump-version-to-2.4.patch
+++ b/patches/buildroot/2022.02/419-package-rt-tests-bump-version-to-2.5.patch
@@ -1,8 +1,8 @@
-From d889bdc9b327203f75a31350f49b21e9da08b495 Mon Sep 17 00:00:00 2001
+From 14601da51ffe8556dee67ab2c05972569e03445f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Szymanski?=
<seb...@ar...>
-Date: Mon, 18 Jul 2022 10:43:34 +0200
-Subject: [PATCH 1/1] package/rt-tests: bump version to 2.4
+Date: Thu, 23 Feb 2023 09:42:59 +0100
+Subject: [PATCH 1/1] package/rt-tests: bump version to 2.5
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -14,28 +14,28 @@ Signed-off-by: Sébastien Szymanski <seb...@ar...>
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/package/rt-tests/rt-tests.hash b/package/rt-tests/rt-tests.hash
-index 3adda60f42..49df8313f9 100644
+index 49df8313f9..eb28f4d441 100644
--- a/package/rt-tests/rt-tests.hash
+++ b/package/rt-tests/rt-tests.hash
@@ -1,4 +1,4 @@
# From https://mirrors.edge.kernel.org/pub/linux/utils/rt-tests/sha256sums.asc
--sha256 b5e7959bcb5c703b2743030751af975ea4e04962e29fc1118f4b605987585735 rt-tests-2.3.tar.xz
-+sha256 393457f0c91cce57e82b876ecb9b672871f898e6cecafc751548079512bcc808 rt-tests-2.4.tar.xz
+-sha256 393457f0c91cce57e82b876ecb9b672871f898e6cecafc751548079512bcc808 rt-tests-2.4.tar.xz
++sha256 2ed2e4c965c7f336a15d3354dec090e27a0b1e9ada91ae0b9ef18bbcb993874e rt-tests-2.5.tar.xz
# locally computed hash
sha256 ab15fd526bd8dd18a9e77ebc139656bf4d33e97fc7238cd11bf60e2b9b8666c6 COPYING
diff --git a/package/rt-tests/rt-tests.mk b/package/rt-tests/rt-tests.mk
-index 056a4a6723..74290211ce 100644
+index 74290211ce..8ad4215ee1 100644
--- a/package/rt-tests/rt-tests.mk
+++ b/package/rt-tests/rt-tests.mk
@@ -6,7 +6,7 @@
RT_TESTS_SITE = $(BR2_KERNEL_MIRROR)/linux/utils/rt-tests
RT_TESTS_SOURCE = rt-tests-$(RT_TESTS_VERSION).tar.xz
--RT_TESTS_VERSION = 2.3
-+RT_TESTS_VERSION = 2.4
+-RT_TESTS_VERSION = 2.4
++RT_TESTS_VERSION = 2.5
RT_TESTS_LICENSE = GPL-2.0+
RT_TESTS_LICENSE_FILES = COPYING
--
-2.35.1
+2.39.2
diff --git a/software/uboot_recover/apf27_recover.py b/software/uboot_recover/apf27_recover.py
index df05c6bfa..8020b2a48 100644
--- a/software/uboot_recover/apf27_recover.py
+++ b/software/uboot_recover/apf27_recover.py
@@ -1,7 +1,7 @@
#
# APF27 bootloader recovering tool
#
-# Copyright (C) 2008-2009 ARMadeus Systems
+# Copyright (C) 2008-2023 Armadeus Systems <su...@ar...>
#
# 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
@@ -20,16 +20,16 @@
# author: nic...@ar...
#
# Change History:
-# - (Jorasse) : init DDR RAM and u-boot upload
+# - (Sébastien Szymanski) : python3 support and cleanup
+# - (Jorasse) : init DDR RAM and u-boot upload
#
-import sys
-import time
-from struct import *
import os
-import serial
-import thread
import re
+import serial
+import sys
+import time
+
class Error(Exception):
""" Manage specific error
@@ -53,150 +53,95 @@ class Error(Exception):
self._message = message
message = property(_get_message, _set_message)
-
def __str__(self):
- print self.message
+ print(self.message)
def setLevel(self,level):
self.level = int(str(level))
def getLevel(self):
return self.level
+
class apf27Bootloader:
statuscode = { 0x8D: 'data specified is out of bounds',
0x55: 'error during assert verification',
- 0x36: 'hash verification failed',
- 0x33: 'certificate parsing failed or the certificate contained an unsupported key',
- 0x35: 'signature verification failed',
- 0x4B: 'CSF command sequence contains unsupported command identifier',
- 0x4E: 'absence of expected CSF header',
- 0x4D: 'CSF length is unsupported',
- 0x2E: 'CSF TYPE does not match processor TYPE',
- 0x2D: 'CSF UID does not match either processor UID or generic UID',
- 0x3A: 'CSF customer/product code does not match processor customer/product code',
- 0x87: 'key indexis either unsupported, or an attempt is made to overwrite the SRK from a CSF command',
- 0xF0: 'successful operation completion',
- 0x17: 'SCC unexpectedly not in secure state',
- 0x1E: 'secureRAM secret key invalid',
- 0x1D: 'secureRAM initialization failure',
- 0x1B: 'secureRAM self test failure',
- 0x2B: 'secureRAM internal failure',
- 0x27: 'secureRAM secrect key unexpectedly in use',
- 0x8B: 'an attempt is made to read a key from the list of subordinate public keys at a location where no key is installed',
- 0x8E: 'algorithm type is either invalid or ortherwise unsupported',
- 0x66: 'write operation to register failed',
- 0x63: 'DCD invalid',
- 0x6F: 'RAM application pointer is NULL or ERASED_FLASH',
- 0x69: 'CSF missing when HAB TYPE is not HAB-disabled',
- 0x6A: 'NANDFC boot buffer load failed',
- 0x6C: 'Exception has occured',
+ 0x36: 'hash verification failed',
+ 0x33: 'certificate parsing failed or the certificate contained an unsupported key',
+ 0x35: 'signature verification failed',
+ 0x4B: 'CSF command sequence contains unsupported command identifier',
+ 0x4E: 'absence of expected CSF header',
+ 0x4D: 'CSF length is unsupported',
+ 0x2E: 'CSF TYPE does not match processor TYPE',
+ 0x2D: 'CSF UID does not match either processor UID or generic UID',
+ 0x3A: 'CSF customer/product code does not match processor customer/product code',
+ 0x87: 'key indexis either unsupported, or an attempt is made to overwrite the SRK from a CSF command',
+ 0xF0: 'successful operation completion',
+ 0x17: 'SCC unexpectedly not in secure state',
+ 0x1E: 'secureRAM secret key invalid',
+ 0x1D: 'secureRAM initialization failure',
+ 0x1B: 'secureRAM self test failure',
+ 0x2B: 'secureRAM internal failure',
+ 0x27: 'secureRAM secrect key unexpectedly in use',
+ 0x8B: 'an attempt is made to read a key from the list of subordinate public keys at a location where no key is installed',
+ 0x8E: 'algorithm type is either invalid or ortherwise unsupported',
+ 0x66: 'write operation to register failed',
+ 0x63: 'DCD invalid',
+ 0x6F: 'RAM application pointer is NULL or ERASED_FLASH',
+ 0x69: 'CSF missing when HAB TYPE is not HAB-disabled',
+ 0x6A: 'NANDFC boot buffer load failed',
+ 0x6C: 'Exception has occured',
0x67: 'INT_BOOT fuse is blown but BOOT pins are set for external boot',
0x88: 'Successful download completion' }
-
-
- def __init__(self, serialport ):
+ def __init__(self, serialport):
self.port = serialport
-
def getstatus(self):
self.port.flushInput()
- #print 'get Status'
- self.port.write( self.buildcmd("0505" ,"00000000","b","00000000","00","0" ))
+ self.port.write(self.buildcmd(0x0505, 0, 0, 0, 0, 0))
result = self.port.read(4)
if len(result) > 0:
try:
- print self.statuscode[ord(result[0])]
- #for i in range(len(result)):
- # print '%02x' % ord(result[i]),
- except KeyError,e:
- print "Unknown status "+str(ord(result[0]))
+ print(self.statuscode[result[0]])
+ except KeyError as e:
+ print("Unknown status {}".format(result[0]))
else:
- print '\nread timeout'
+ print('\nread timeout')
def __displayProgress(self, text):
sys.stdout.write(chr(13))
- print text,
+ print(text, end=' ')
sys.stdout.flush()
-# addr: address to read
-# count: number of bytes to read
-# mode: b, w, l for byte, word, long
-
- def get( self, addr, count, mode ):
- assert len(addr) <= 8, 'address too long'
- assert int(count,16) > 0, 'zero length get'
- assert int(count,16) < 0xffffffff, 'Too long get, should be < 31 bytes'
- self.port.write( self.buildcmd("0101" ,addr,mode,count.zfill(9-len(count)),"00000000","0" ) )
-
- #nbbyte = 1
- #if mode == 'w':
- # nbbyte = 2
- #if mode == 'l':
- # nbbyte = 4
-
- #nbbyte = nbbyte * int(count)
- temp = self.port.read(int(count)+4) # read n+4 char
- if len(temp) == 0:
- print 'read timeout'
- return
- #print '\nread ACK:',
- #for i in range (len(temp)):
- # print '%02x' % ord(temp[i]),
-
- #print 'read data:',
- result = []
- for i in range (4,len(temp)):
-
- result += '%02x' % ord(temp[len(temp)-i+3]),
- #print ''.join(result)
- return ''.join(result)
-
-
# addr: address where the data have to be written
# data: data to write. must match the given mode
+# mode: 0x08, 0x10, 0x20 for byte, word, long
-# mode: b, w, l for byte, word, long
-
- def put( self, addr, data, mode ):
- assert len(data) > 0, 'zero length put'
- assert len(addr) == 8, 'address too long'
- self.port.write( self.buildcmd("0202" ,addr,mode,"00000000",data,"0" ))
- result = self.port.read(4) #ack
- if len(result) == 0:
- print 'read timeout'
+ def put(self, addr, data, mode):
+ self.port.write(self.buildcmd(0x0202, addr, mode, 0, data, 0))
+ result = self.port.read(4) # ack
+ if len(result) == 0:
+ print('read timeout')
return
- #print ' \nwrite ACK:',
- #for i in range (len(result)):
- # print '%02x' % ord(result[i]),
result = self.port.read(4)
if len(result) == 0:
- print 'read timeout'
- return
-
- #print '\nwrite success:',
- #for i in range (len(result)):
- #print '%02x' % ord(result[i]),
-
+ print('read timeout')
- def download( self, addr, data, size):
+ def download(self, addr, data, size):
assert size > 0, 'file empty'
f=open(data, 'rb')
- self.port.write( self.buildcmd("0404" ,addr,"l", size, "00000000","AA" ))
- result = self.port.read(4) #ack
- if len(result) == 0:
- print '\nread timeout'
+ self.port.write(self.buildcmd(0x0404, addr, 0, size, 0, 0xAA))
+ result = self.port.read(4) # ack
+ if len(result) == 0:
+ print('\nread timeout')
return
- #print ' \nwrite ACK:',
- #for i in range (len(result)):
- # print '%02x' % ord(result[i]),
data = f.read(16)
count = 16
b = 0
- print '\nTransfering:'
+ print('\nTransfering:')
while len(data):
n = len(data)
b = 16-n # ensure always 4 bytes
@@ -208,84 +153,58 @@ class apf27Bootloader:
data = f.read(16)
time.sleep(0.001)
self.__displayProgress("%d bytes transfered" % count)
- print ""
+ print("")
f.close()
self.getstatus()
- return
-
- def buildcmd( self, cmd, addr, ds, cnt, data, ft ):
- if ds == 'b':
- datatmp = ('%c' % int('00',16)) + ('%c' % int('00',16)) + ('%c' % int('00',16)) + ('%c' % int(data[0:2],16))
- datasize = "08"
- elif ds == 'w':
- datasize = "10"
- datatmp = ('%c' % int('00',16)) + ('%c' % int('00',16)) + ('%c' % int(data[0:2],16)) + ('%c' % int(data[2:4],16))
- elif ds == 'l':
- datasize = "20"
- datatmp = ('%c' % int(data[0:2],16)) + ('%c' % int(data[2:4],16)) + ('%c' % int(data[4:6],16)) + ('%c' % int(data[6:8],16))
- else:
- print "error invalid data size"
- return ""
-
- result = ('%c' % int(cmd[0:2],16)) + ('%c' % int(cmd[2:4],16)) + \
- ('%c' % int(addr[0:2],16)) + ('%c' % int(addr[2:4],16)) + ('%c' % int(addr[4:6],16)) + ('%c' % int(addr[6:8],16)) + \
- ('%c' % int(datasize,16)) + \
- ('%c' % int(cnt[0:2],16)) + ('%c' % int(cnt[2:4],16)) + ('%c' % int(cnt[4:6],16)) + ('%c' % int(cnt[6:8],16)) + \
- datatmp + ('%c' % int(ft,16))
- #for i in range(len(result)):
- # print '%02x' % ord(result[i]),
- #print cnt
- return result
+ def buildcmd(self, cmd, addr, ds, cnt, data, ft):
+ result = b''.join([cmd.to_bytes(2, 'big'), addr.to_bytes(4, 'big'),
+ ds.to_bytes(1, 'big'), cnt.to_bytes(4, 'big'),
+ data.to_bytes(4, 'big'), ft.to_bytes(1, 'big')])
+ return result
def initSDRAM2( self ):
- print "SDRAM INIT2 APF",
- self.put("D800100C","0069D42A","l")
-
- self.put("D8001008","92126485","l")
- self.put("B0000400","00000000","l")
- self.put("D8001008","A2126485","l")
- self.put("B0000000","00000000","l")
- self.put("B0000000","00000000","l")
- self.put("B0000000","00000000","l")
- self.put("B0000000","00000000","l")
- self.put("D8001008","B2126485","l")
- self.put("B0000033","DA0000DA","b")
- self.put("B2000000","00000000","b")
- self.put("D8001008","82126485","l")
+ print("SDRAM INIT2 APF", end=' ')
+ self.put(0xD800100C, 0x0069D42A, 0x20)
+
+ self.put(0xD8001008, 0x92126485, 0x20)
+ self.put(0xB0000400, 0x00000000, 0x20)
+ self.put(0xD8001008, 0xA2126485, 0x20)
+ self.put(0xB0000000, 0x00000000, 0x20)
+ self.put(0xB0000000, 0x00000000, 0x20)
+ self.put(0xB0000000, 0x00000000, 0x20)
+ self.put(0xB0000000, 0x00000000, 0x20)
+ self.put(0xD8001008, 0xB2126485, 0x20)
+ self.put(0xB0000033, 0xDA0000DA, 0x08)
+ self.put(0xB2000000, 0x00000000, 0x08)
+ self.put(0xD8001008, 0x82126485, 0x20)
def initSDRAM1( self ):
+ print("SDRAM INIT1 APF", end=' ')
+ self.put(0x10000000, 0x20040304, 0x20) #/* AIPI1_PSR0 */
+ self.put(0x10000004, 0xDFFBFCFB, 0x20) #/* AIPI1_PSR1 */
+ self.put(0x10020000, 0x00000000, 0x20) #/* AIPI2_PSR0 */
+ self.put(0x10020004, 0xFFFFFFFF, 0x20) #/* AIPI2_PSR1 */
+ self.put(0x10027818, 0x0003000F, 0x20) #/* GPCR */
- print "SDRAM INIT1 APF",
- self.put("10000000","20040304","l") #/* AIPI1_PSR0 */
- self.put("10000004","DFFBFCFB","l") #/* AIPI1_PSR1 */
- self.put("10020000","00000000","l") #/* AIPI2_PSR0 */
- self.put("10020004","FFFFFFFF","l") #/* AIPI2_PSR1 */
- self.put("10027818","0003000F","l") #/* GPCR */
-
- self.put("D8002060","00002200","l") #/* EIM / WCR */
-
- self.put("D8001010","0000002C","l")
+ self.put(0xD8002060, 0x00002200, 0x20) #/* EIM / WCR */
- self.put("D8001004","0069D42A","l")
+ self.put(0xD8001010, 0x0000002C, 0x20)
- self.put("D8001000","92126485","l")
- self.put("A0000400","00000000","l")
- self.put("D8001000","A2126485","l")
- self.put("A0000000","00000000","l")
- self.put("A0000000","00000000","l")
- self.put("A0000000","00000000","l")
- self.put("A0000000","00000000","l")
- self.put("D8001000","B2126485","l")
- self.put("A0000033","DA0000DA","b")
- self.put("A2000000","00000000","b")
- self.put("D8001000","82126485","l")
+ self.put(0xD8001004, 0x0069D42A, 0x20)
+ self.put(0xD8001000, 0x92126485, 0x20)
+ self.put(0xA0000400, 0x00000000, 0x20)
+ self.put(0xD8001000, 0xA2126485, 0x20)
+ self.put(0xA0000000, 0x00000000, 0x20)
+ self.put(0xA0000000, 0x00000000, 0x20)
+ self.put(0xA0000000, 0x00000000, 0x20)
+ self.put(0xA0000000, 0x00000000, 0x20)
+ self.put(0xD8001000, 0xB2126485, 0x20)
+ self.put(0xA0000033, 0xDA0000DA, 0x08)
+ self.put(0xA2000000, 0x00000000, 0x08)
+ self.put(0xD8001000, 0x82126485, 0x20)
- def consoleDump(tid):
- while 1:
- sys.stdout.write(tid.read(1))
- # print '%s' % tid.read(1),
class UBoot:
"""
@@ -297,52 +216,49 @@ class UBoot:
def __getOutput(self,error_msg=None):
""" This function waits for U-Boot response and return response"""
- #debugser = serial.Serial("/dev/tty0",115200,timeout=1)
response = ""
while not re.search(r'BIOS>',response):
char = self.serial.read(1)
if char == '':
raise Error(response)
- response = response + char
+ response = response + char.decode("ascii")
if error_msg!=None:
if re.search(error_msg,response):
raise Error("getOutput error")
- sys.stdout.write(char)
- #debugser.write(char)
- #debugser.close()
+ sys.stdout.write(char.decode("ascii"))
return response
def load(self):
if not os.path.exists(self.binary):
raise Error("file "+self.binary+" doesn't exit",0)
- self.fsize = "%08x" % os.path.getsize(self.binary) #os.stat(filesize)[6]
- self.bootstrap.download("A0000000", self.binary, self.fsize)
+ self.fsize = os.path.getsize(self.binary)
+ self.bootstrap.download(0xA0000000, self.binary, self.fsize)
def resetenv(self):
self.serial.flushInput()
- self.serial.write("env default -f && saveenv && echo Flash environment variables erased!\n")
+ self.serial.write("env default -f && saveenv && echo Flash environment variables erased!\n".encode("ascii"))
return self.__getOutput()
def waitForPrompt(self):
- self.serial.write("\003")
+ self.serial.write("\003".encode("ascii"))
while not self.serial.inWaiting():
time.sleep(0.001)
- self.serial.write("\003")
+ self.serial.write("\003".encode("ascii"))
return self.__getOutput()
def flash(self):
""" flash uboot """
self.serial.flushInput()
- self.serial.write("setenv fileaddr A0000000; setenv filesize "+self.fsize+";run flash_uboot\n")
+ self.serial.write("setenv fileaddr A0000000; setenv filesize {:08x};run flash_uboot\n".format(self.fsize).encode("ascii"))
try:
ret = self.__getOutput()
- except Error,e:
+ except Error as e:
raise Error("Flashing U-Boot")
- if not re.search(r'uboot succeed',ret):
- print ret
- raise Error("U-Boot flash failed",0)
+ if not re.search(r'uboot succeed', ret):
+ print(ret)
+ raise Error("U-Boot flash failed", 0)
return ret
-
+
if __name__ == "__main__":
@@ -353,27 +269,27 @@ if __name__ == "__main__":
UBOOT_RECOVERY_PATH = RECOVERY_URL+RECOVERY_VERSION+"/"+RECOVERY_BOARD_PATH+UBOOT_IMAGE_BIN+"/download"
if not os.path.exists(UBOOT_IMAGE_BIN):
try:
- print "Downloading " + UBOOT_IMAGE_BIN + ".."
+ print("Downloading " + UBOOT_IMAGE_BIN + "..")
os.system("wget -O "+ UBOOT_IMAGE_BIN + " " +UBOOT_RECOVERY_PATH)
- except Exception, msg:
- print "Failed to download U-Boot image file from server!"
- print msg
- print "Server may be unavailable for the momenet. please try later."
+ except Exception as msg:
+ print("Failed to download U-Boot image file from server!")
+ print(msg)
+ print("Server may be unavailable for the momenet. please try later.")
os.remove(UBOOT_IMAGE_BIN);
sys.exit()
SPEED = 115200
- print "\nAPF27 Bootstrap Tool\n"
- print "/!\\ Do not forget to put the bootstrap jumper and to reset your board /!\\"
- print "(be sure to have a file named apf27-u-boot-nand.bin in current dir too)\n"
+ print("\nAPF27 Bootstrap Tool\n")
+ print("/!\\ Do not forget to put the bootstrap jumper and to reset your board /!\\")
+ print("(be sure to have a file named apf27-u-boot-nand.bin in current dir too)\n")
print("/!\\ U-Boot recover eat lot of CPU, be sure that no process trust your CPU\n")
- port = raw_input('Enter serial port number or name to use (/dev/ttySx under Linux and COMx under Windows): ')
+ port = input('Enter serial port number or name to use (/dev/ttySx under Linux and COMx under Windows): ')
try:
ser = serial.Serial(port, SPEED, timeout=3)
- except Exception, msg:
- print "unable to open serial port %s" % port
- print msg
+ except Exception as msg:
+ print("unable to open serial port %s" % port)
+ print(msg)
sys.exit()
ser.flush()
@@ -385,11 +301,8 @@ if __name__ == "__main__":
apfBootloader.initSDRAM2()
uboot.load()
uboot.waitForPrompt()
- eraseAll = raw_input('Would you like to erase the environment variables? y/n: ')
+ eraseAll = input('Would you like to erase the environment variables? y/n: ')
if eraseAll == 'y':
uboot.resetenv()
uboot.flash()
- print "U-Boot successfully recovered !"
-
- del ser
-
+ print("U-Boot successfully recovered !")
\ No newline at end of file
hooks/post-receive
--
armadeus
|