feat: approximate NVRAM requirement from the CAP analysis (v3.5.15)
GlobalPlatform models this explicitly (GP Card Spec v2.3.1 Table 11-48, load parameters): C6 = non-volatile code, C7 = volatile data, C8 = non-volatile data, and 11.5.2.3.7 - when the card makes no code/data distinction the required minimum is C6 + C8. The analysis now reports it: - capmem.memory_json() accepts the load file size (all CAP components - the package image the card stores) and returns code.load_file, nvram.requirement = load_file + persistent data, and sets the suggested C6 to the load file (the bytecode-only Method.cap figure stays in code.method_component); _cap_info_body passes the size it already computed. - The CAP estimate box (both the RAM installer and the SCP81 Install-from-.cap form) leads with "NVRAM requirement ≈ code image + data" plus the RAM estimate, keeps the bytecode/other-component split and the full data breakdown in Details, and carries the GP citation with the caveats (card memory management, allocation rounding and the registry entry are not included; the static field image appears in both parts). - An older server response without code.load_file still renders (the bytecode size is used as the fallback). Tests: Python +1 (load-file semantics) with extended cap-info assertions, frontend +1 (renderer + fallback). Help EN/RU, docs/api.md, AGENTS. 587 frontend / 472 Python green; version 3.5.15; sw simple-v268.
This commit is contained in:
@@ -1629,11 +1629,24 @@ def compute_memory(report):
|
||||
return result
|
||||
|
||||
|
||||
def memory_json(report, memory):
|
||||
"""Compact, stable JSON shape for the API/PWA (all byte counts)."""
|
||||
def memory_json(report, memory, load_file_bytes=None):
|
||||
"""Compact, stable JSON shape for the API/PWA (all byte counts).
|
||||
|
||||
``load_file_bytes`` is the concatenated CAP load file (all components) -
|
||||
the package image the card stores, our proxy for the GP Card Spec v2.3.1
|
||||
Table 11-48 "non-volatile code" minimum memory requirement. With it the
|
||||
suggested C6 becomes the load file and ``nvram.requirement`` reports the
|
||||
C6+C8-style total (code image + persistent data, per 11.5.2.3.7: "If both
|
||||
tags 'C6' and 'C8' are present and the implementation does not make any
|
||||
distinction between Non-Volatile Code and Non-Volatile Data Memory then
|
||||
the required minimum shall be the sum of both values"); without it the
|
||||
tool's bytecode-only C6 is kept."""
|
||||
ram_transient = memory.get('ram_transient_arrays', 0)
|
||||
ram_runtime = memory.get('runtime_transient_bytes', 0)
|
||||
ram_frame = memory.get('ram_frame_bytes', 0)
|
||||
nvram_total = memory.get('nvram_with_ref_storage', 0)
|
||||
load_file = max(0, int(load_file_bytes or 0))
|
||||
method_component = report.get('method_component_size', 0)
|
||||
return {
|
||||
'cap_version': report.get('cap_version'),
|
||||
'package_version': report.get('package_version'),
|
||||
@@ -1643,7 +1656,8 @@ def memory_json(report, memory):
|
||||
'class_count': report.get('class_count', 0),
|
||||
'method_count': report.get('method_count', 0),
|
||||
'code': {
|
||||
'method_component': report.get('method_component_size', 0),
|
||||
'method_component': method_component,
|
||||
'load_file': load_file,
|
||||
},
|
||||
'nvram': {
|
||||
'static_image': memory.get('nvram_static_image', 0),
|
||||
@@ -1651,8 +1665,11 @@ def memory_json(report, memory):
|
||||
'install_objects': memory.get('nvram_persistent_objects', 0),
|
||||
'header_overhead': memory.get('nvram_object_header_overhead', 0),
|
||||
'ref_storage': memory.get('reference_storage', 0),
|
||||
'total': memory.get('nvram_with_ref_storage', 0),
|
||||
'total': nvram_total,
|
||||
'runtime': memory.get('runtime_persistent_bytes', 0),
|
||||
# C6+C8-style total: package image + persistent data (None when
|
||||
# the caller did not provide the load file size)
|
||||
'requirement': (load_file + nvram_total) if load_file else None,
|
||||
},
|
||||
'ram': {
|
||||
'transient_arrays': ram_transient,
|
||||
@@ -1661,9 +1678,9 @@ def memory_json(report, memory):
|
||||
'total': ram_transient + ram_runtime + ram_frame,
|
||||
},
|
||||
'suggested': {
|
||||
'c6': report.get('method_component_size', 0),
|
||||
'c6': load_file or method_component,
|
||||
'c7': ram_transient + 256,
|
||||
'c8': memory.get('nvram_with_ref_storage', 0),
|
||||
'c8': nvram_total,
|
||||
},
|
||||
'warnings': list(report.get('warnings', [])),
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.5.14'
|
||||
VERSION = '3.5.15'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -2564,7 +2564,9 @@ def _cap_info_body(body):
|
||||
return {'ok': False, 'error': 'cap parse failed: %s' % e}
|
||||
try:
|
||||
report, memory = capmem.analyze_bytes(bytes.fromhex(cap_hex))
|
||||
info = capmem.memory_json(report, memory)
|
||||
# the load file (all components) is our proxy for the GP "non-volatile
|
||||
# code" requirement, so the JSON can report the C6+C8-style total
|
||||
info = capmem.memory_json(report, memory, load_file_bytes=len(loadfile_data) // 2)
|
||||
except Exception as e:
|
||||
return {'ok': False, 'error': 'cap analysis failed: %s' % e}
|
||||
return {'ok': True, 'load_file_aid': loadfile_aid, 'module_aid': module_aid,
|
||||
|
||||
Reference in New Issue
Block a user