<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.campana.vi.it/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.campana.vi.it/" rel="alternate" type="text/html" /><updated>2026-02-11T20:52:11+00:00</updated><id>https://www.campana.vi.it/feed.xml</id><title type="html">Ottavio Campana</title><subtitle>Memento audere semper</subtitle><entry><title type="html">Domotica accessibile con l’AI</title><link href="https://www.campana.vi.it/blog/domotica-accessibile-ipovedenti/" rel="alternate" type="text/html" title="Domotica accessibile con l’AI" /><published>2026-02-11T00:00:00+00:00</published><updated>2026-02-11T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/domotica-accessibile-ipovedenti</id><content type="html" xml:base="https://www.campana.vi.it/blog/domotica-accessibile-ipovedenti/"><![CDATA[<p>Qualche giorno fa, durante una cena con un vecchio amico che non vedevo da anni, mi ha raccontato di un progetto a cui stava lavorando: automatizzare la domotica di casa sua per renderla controllabile tramite voce, con l’obiettivo di migliorare la qualità della vita di sua figlia, purtroppo ipovedente. Mentre ascoltavo le sue idee, mi sono reso conto che conoscevo già tutti i “mattoncini” necessari per costruire una soluzione del genere. Così, dopo averla testata a casa mia, ho deciso di scrivere questa guida per condividerla con lui e con chiunque altro si trovi ad affrontare una sfida simile.</p>

<h3 id="la-soluzione-in-breve">La soluzione in breve</h3>

<p>L’idea è semplice: usare un sistema domotico open-source (Home Assistant) integrato con modelli di intelligenza artificiale per interpretare comandi vocali, elaborare risposte e fornire feedback audio. Il tutto senza dipendere da servizi cloud proprietari (come Alexa o Google Assistant), garantendo maggiore privacy e personalizzazione.</p>

<p>I componenti chiave sono:</p>
<ul>
  <li>Home Assistant (versione 2026.2.1) come hub centrale.</li>
  <li>Due addon di Home Assistant per l’AI:
    <ul>
      <li>Local OpenAI LLM (1.3.1) per gestire il modello linguistico.</li>
      <li>OpenAI Whisper Cloud (1.3.2) per il riconoscimento vocale.</li>
    </ul>
  </li>
  <li><a href="https://mistral.ai/">Mistral AI</a> come fornitore del modello linguistico (più economico e compliant con il GDPR e l’EU AI Act che diventerà cogente in Agosto 2026).</li>
  <li>PicoTTS per la sintesi vocale di base (integrato nativamente in Home Assistant).</li>
</ul>

<h3 id="passo-1-configurare-mistral-ai">Passo 1: Configurare Mistral AI</h3>

<p>Prima di tutto, è necessario registrarsi su Mistral AI e generare una chiave API all’indirizzo <a href="https://console.mistral.ai/home">https://console.mistral.ai/home</a>. Per comodità, io l’ho chiamata <code class="language-plaintext highlighter-rouge">homeassistant</code>, ma potete usare il nome che preferite. Questa chiave servirà per collegare Home Assistant ai servizi di Mistral.</p>

<p>Attenzione: Mistral, come molti provider di inferenza, ha una offerta variegata. Offre un livello gratuito con crediti limitati (verificate le condizioni aggiornate sul loro sito), ha la possibilità di funzionare in abbonamento con un canone mensile e supporta una modalità di pagamento a consumo, chiamata <strong>API</strong>. In generale, poiché non voglio essere legato ad un tool di un venditore specifico, io opto sempre per la fatturazione a consumo dell’accesso mediante API. Questo tipo di accesso permette spesso di pagare di meno di un abbonamento. Ad esempio, il modello <code class="language-plaintext highlighter-rouge">mistral-small-latest</code> (quello che sto usando io) ha un prezzo accessibile e funziona egregiamente per questo scopo. Trovate tutte le informazioni sui prezzi dell’uso delle API <a href="https://mistral.ai/pricing#api">qua</a>.</p>

<h3 id="passo-2-installare-e-configurare-gli-addon-in-home-assistant">Passo 2: Installare e configurare gli addon in Home Assistant</h3>

<h4 id="openai-whisper-cloud">OpenAI Whisper Cloud</h4>

<p>Installate l’addon da HACS di Home Assistant. Nella configurazione, selezionate <code class="language-plaintext highlighter-rouge">Mistral</code> come fornitore del servizio. Incollate la chiave API generata in precedenza. In automatico il sistema vi proporrà di usare il modello <code class="language-plaintext highlighter-rouge">voxtral-mini-latest</code>.</p>

<h4 id="local-openai-llm">Local OpenAI LLM</h4>

<p>Anche qui, installate l’addon (versione 1.3.1 o successiva) da HACS. <strong>Attenzione</strong>: versioni precedenti dell’addon non funzionano con Mistral (si veda questa <a href="https://github.com/skye-harris/hass_local_openai_llm/issues/22">issue</a>) su Github. Nella configurazione, oltre alla chiave API, dovete specificare l’URL del servizio Mistral: <code class="language-plaintext highlighter-rouge">https://api.mistral.ai/v1/</code>.</p>

<p>Se l’installazione non crea automaticamente un <em>conversation agent</em>, aggiungetelo manualmente con l’apposito pulsante nella schermata delle integrazioni. E’ possibile creare anche più di un agente di conversazione, basandosi su diversi modelli (e costi). Come vi dicevo, io uso <code class="language-plaintext highlighter-rouge">mistral-small-latest</code>.</p>

<p>I modelli di Mistral hanno la caratteristica di non obbedire sempre alle richieste di formattazione e troppo spesso tendono a generare testo in formato markdown. Questa formattazione non va bene con i sistemi Text-To-Speech (TTS), che finiscono con leggere i tag del markup. Per ovviare a questo nella scheda dell’integrazione di Local OpenAI LLM è necessario clickare sul simbolo di configurazione dell’agente di conversazione e modificare la configurazione dell’agente nel modo seguente:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>You are a voice assistant for Home Assistant.
Answer questions about the world truthfully.
Answer in plain text. Never use markdown. Keep it simple and to the point.
</code></pre></div></div>

<p>Può sembrare poco, ma l’aggiunta di <code class="language-plaintext highlighter-rouge">Never use markdown.</code> fa la differenza.</p>

<h4 id="passo-3-abilitare-la-sintesi-vocale-con-picotts">Passo 3: Abilitare la sintesi vocale con PicoTTS</h4>

<p>PicoTTS è un motore di text-to-speech (TTS) basilare ma funzionale per testare la soluzione, perché in questa applicazione ci interessa di più capire il linguaggio naturale dell’utente ed eseguire azioni, piuttosto che fare conversazione. PicoTTS ha una voce robotica e limitata perché è pensato per sistemi embedded. Per attivarlo, modificate il file <code class="language-plaintext highlighter-rouge">configuration.yaml</code> di Home Assistant aggiungendo:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tts:
    - platform: picotts
</code></pre></div></div>

<p>Riavviate Home Assistant, in modo da abilitare tutti i componenti.</p>

<h4 id="passo-4-collegare-i-puntini-in-home-assistant">Passo 4: Collegare i puntini in Home Assistant</h4>

<p>Ora è il momento di unire tutto:</p>
<ul>
  <li>Andate in <code class="language-plaintext highlighter-rouge">Impostazioni</code> &gt; <code class="language-plaintext highlighter-rouge">Assistenti vocali</code>.</li>
  <li>Verificate che ci sia un assistente predefinito chiamato <code class="language-plaintext highlighter-rouge">Home Assistant</code>. Qualora non fosse presente si può aggiungerne uno</li>
  <li>Nella sezione <code class="language-plaintext highlighter-rouge">AI Agent</code>, scegliete quello che avete creato con il modello che avete selezionato configurando Local OpenAI LLM (<code class="language-plaintext highlighter-rouge">mistral-small-latest</code> in questo esempio) e disabilitate la gestione dei comandi in locale (altrimenti bypassereste l’AI).</li>
  <li>In <code class="language-plaintext highlighter-rouge">Riconoscimento vocale</code>, impostate <code class="language-plaintext highlighter-rouge">Mistral AI Whisper</code>.</li>
  <li>Infine, in <code class="language-plaintext highlighter-rouge">Sintesi vocale</code>, selezionate <code class="language-plaintext highlighter-rouge">PicoTTS</code>.</li>
</ul>

<p>Dovreste ritrovarvi con una configurazione simile a questa:</p>

<p><img src="/images/20260211-impostazioni-assistente-vocale.png" alt="configurazione assistente vocale home assistant" /></p>

<h4 id="testare-la-soluzione">Testare la soluzione</h4>

<p>A questo punto, potete usare l’interfaccia web o la companion app di Home Assistant (disponibile per Android e iOS) per interagire con il sistema. In alto destra c’è la voce <code class="language-plaintext highlighter-rouge">Assist</code> (attenzione, nella app potrebbe essere collassata nell’icone dei re puntini) con cui sarà possibile avviare una conversazione. <strong>Attenzione</strong>: potrebbe accadere che su Firefox non vi funzioni il microfono. Questo accade perché siete connessi all’interfaccia web mediante HTTP e non HTTPS.</p>

<p>Qualora il sistema vi piaccia, potete automatizzare i comandi vocali con <a href="https://www.home-assistant.io/voice-pe/">Voice</a>, lo smart speaker ufficiale di Home Assistant.</p>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[una soluzione vocale per ipovedenti (e non solo)]]></summary></entry><entry><title type="html">La densità di pixel e la norma EN/IEC 62676-4</title><link href="https://www.campana.vi.it/articles/densita-pixel/" rel="alternate" type="text/html" title="La densità di pixel e la norma EN/IEC 62676-4" /><published>2019-08-22T00:00:00+00:00</published><updated>2019-08-22T00:00:00+00:00</updated><id>https://www.campana.vi.it/articles/densita-pixel</id><content type="html" xml:base="https://www.campana.vi.it/articles/densita-pixel/"><![CDATA[<h2 id="calcolatore">Calcolatore</h2>

<p>Per gli impazienti, ecco un calcolatore per determinare le prestazioni di un sistema di visione, secondo la norma EN/IEC 62676-4.</p>

<div>
<table>
  <tr>
    <th>Telecamera</th>
    <td>
      <select id="telecamera" name="telecamera" style="width:100%;">
        <option value="videotec_vcmhd30x01">Videotec VCMHD30X01</option>
        <option value="sony_fcb_ev7100">Sony FCB-EV7100</option>
        <option value="sony_fcb_ev7300">Sony FCB-EV7300</option>
        <option value="sony_fcb_ev7500">Sony FCB-EV7500</option>
        <option value="sony_fcb_ev7520">Sony FCB-EV7520</option>
        <option value="sony_fcb_se600">Sony FCB-SE600</option>
        <option value="sony_fcb_ex1020p">Sony FCB-EX1020P</option>
        <option value="sony_fcb_ex1020">Sony FCB-EX1020</option>
        <option value="flir_46336013HSPNLX">Flir Tau2 336x256 13mm</option>
        <option value="flir_46640013HSPNLX">Flir Tau2 640x512 13mm</option>
        <option value="flir_46336019HSPNLX">Flir Tau2 336x256 19mm</option>
        <option value="flir_46640019HSPNLX">Flir Tau2 640x512 19mm</option>
        <option value="flir_46336025HSPNLX">Flir Tau2 336x256 25mm</option>
        <option value="flir_46640025HSPNLX">Flir Tau2 640x512 25mm</option>
        <option value="flir_46336035HSPNLX">Flir Tau2 336x256 35mm</option>
        <option value="flir_46640035HSPNLX">Flir Tau2 640x512 35mm</option>
        <option value="flir_46336050HSPNLX">Flir Tau2 336x256 50mm</option>
        <option value="flir_46640050HSPNLX">Flir Tau2 640x512 50mm</option>
        <option value="flir_46633660HSPNLX">Flir Tau2 336x256 60mm</option>
        <option value="flir_46640060HSPNLX">Flir Tau2 640x512 60mm</option>
        <option value="flir_lepton_50">Flir Lepton 80x60 50mm</option>
        <option value="flir_lepton_25">Flir Lepton 80x60 25mm</option>
        <option value="flir_lepton3_50">Flir Lepton3 160x120 50mm</option>
      </select>
    </td>
    <th><div style="overflow: hidden; white-space: nowrap;">Monitor (m)</div></th>
    <td><input id="monitoring" type="text" /></td>
  </tr>
  <tr>
    <th><div style="overflow: hidden; white-space: nowrap;">Risoluzione orizzontale</div></th>
    <td><input type="text" id="risoluzione_orizzontale" name="risoluzione_orizzontale" readonly="" /></td>
    <th><div style="overflow: hidden; white-space: nowrap;">Detect (m)</div></th>
    <td><input id="detection" type="text" /></td>
  </tr>
  <tr>
    <th><div style="overflow: hidden; white-space: nowrap;">Angolo tele orizzontale</div></th>
    <td><input type="text" id="angolo_tele_orizzontale" name="angolo_tele_orizzontale" readonly="" /></td>
    <th><div style="overflow: hidden; white-space: nowrap;">Observe (m)</div></th>
    <td><input id="observation" type="text" /></td>
  </tr>
  <tr>
    <th><div style="overflow: hidden; white-space: nowrap;">Angolo tele verticale</div></th>
    <td><input type="text" id="angolo_tele_verticale" name="angolo_tele_verticale" readonly="" /></td>
    <th><div style="overflow: hidden; white-space: nowrap;">Recognise (m)</div></th>
    <td><input id="recognition" type="text" /></td>
  </tr>
  <tr>
    <th></th>
    <td></td>
    <th><div style="overflow: hidden; white-space: nowrap;">Identify (m)</div></th>
    <td><input id="identification" type="text" /></td>
  </tr>
  <tr>
    <th></th>
    <td></td>
    <th><div style="overflow: hidden; white-space: nowrap;">Inspect (m)</div></th>
    <td><input id="inspect" type="text" /></td>
  </tr>
</table>

<script>
function aggiorna_distanze ()
{
  var res = document.querySelector('#risoluzione_orizzontale').value;
  var angolo = document.querySelector('#angolo_tele_orizzontale').value;

  var d_monitoring = Math.round ( (res / (2 * 12) ) / (Math.tan (angolo * Math.PI / 180.00) ));
  document.querySelector('#monitoring').value = d_monitoring;

  var d_detection = Math.round ( (res / (2 * 25) ) / (Math.tan (angolo * Math.PI / 180.00) ));
  document.querySelector('#detection').value = d_detection;

  var d_observation = Math.round ( (res / (2 * 62) ) / (Math.tan (angolo * Math.PI / 180.00) ));
  document.querySelector('#observation').value = d_observation;

  var d_recognition = Math.round ( (res / (2 * 125) ) / (Math.tan (angolo * Math.PI / 180.00) ));
  document.querySelector('#recognition').value = d_recognition;

  var d_identification = Math.round ( (res / (2 * 250) ) / (Math.tan (angolo * Math.PI / 180.00) ));
  document.querySelector('#identification').value = d_identification;

  var d_inspect = Math.round ( (res / (2 * 1000) ) / (Math.tan (angolo * Math.PI / 180.00) ));
  document.querySelector('#inspect').value = d_inspect;
}

function aggiorna_telecamera (aggiorna_dati)
{
  if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_ev7520')
  {
    document.querySelector('#risoluzione_orizzontale').value = 1920;
    document.querySelector('#angolo_tele_verticale').value = 1.3;
    document.querySelector('#angolo_tele_orizzontale').value = 2.3;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_ev7500')
  {
    document.querySelector('#risoluzione_orizzontale').value = 1920;
    document.querySelector('#angolo_tele_verticale').value = 1.3;
    document.querySelector('#angolo_tele_orizzontale').value = 2.3;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_ev7300')
  {
    document.querySelector('#risoluzione_orizzontale').value = 1920;
    document.querySelector('#angolo_tele_verticale').value = 1.85;
    document.querySelector('#angolo_tele_orizzontale').value = 3.3;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_ev7100')
  {
    document.querySelector('#risoluzione_orizzontale').value = 1920;
    document.querySelector('#angolo_tele_verticale').value = 4.725;
    document.querySelector('#angolo_tele_orizzontale').value = 7.6;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_se600')
  {
    document.querySelector('#risoluzione_orizzontale').value = 1920;
    document.querySelector('#angolo_tele_verticale').value = 18;
    document.querySelector('#angolo_tele_orizzontale').value = 32;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_ex1020p')
  {
    document.querySelector('#risoluzione_orizzontale').value = 720;
    document.querySelector('#angolo_tele_verticale').value = 1.275;
    document.querySelector('#angolo_tele_orizzontale').value = 1.7;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'sony_fcb_ex1020')
  {
    document.querySelector('#risoluzione_orizzontale').value = 720;
    document.querySelector('#angolo_tele_verticale').value = 1.275;
    document.querySelector('#angolo_tele_orizzontale').value = 1.7;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46336013HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 336;
    document.querySelector('#angolo_tele_verticale').value = 9.5;
    document.querySelector('#angolo_tele_orizzontale').value = 12.5;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46640013HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 640;
    document.querySelector('#angolo_tele_verticale').value = 18.5;
    document.querySelector('#angolo_tele_orizzontale').value = 22.5;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46336019HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 336;
    document.querySelector('#angolo_tele_verticale').value = 6.5;
    document.querySelector('#angolo_tele_orizzontale').value = 8.5;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46640019HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 640;
    document.querySelector('#angolo_tele_verticale').value = 13;
    document.querySelector('#angolo_tele_orizzontale').value = 16;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46336025HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 336;
    document.querySelector('#angolo_tele_verticale').value = 5;
    document.querySelector('#angolo_tele_orizzontale').value = 6.5;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46640025HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 640;
    document.querySelector('#angolo_tele_verticale').value = 10;
    document.querySelector('#angolo_tele_orizzontale').value = 12.5;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46336035HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 336;
    document.querySelector('#angolo_tele_verticale').value = 3.55;
    document.querySelector('#angolo_tele_orizzontale').value = 4.65;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46640035HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 640;
    document.querySelector('#angolo_tele_verticale').value = 7;
    document.querySelector('#angolo_tele_orizzontale').value = 9;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46336050HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 336;
    document.querySelector('#angolo_tele_verticale').value = 2.5;
    document.querySelector('#angolo_tele_orizzontale').value = 3.25;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46640050HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 640;
    document.querySelector('#angolo_tele_verticale').value = 4.95;
    document.querySelector('#angolo_tele_orizzontale').value = 6.2;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46336060HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 336;
    document.querySelector('#angolo_tele_verticale').value = 2.1;
    document.querySelector('#angolo_tele_orizzontale').value = 2.75;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_46640060HSPNLX')
  {
    document.querySelector('#risoluzione_orizzontale').value = 640;
    document.querySelector('#angolo_tele_verticale').value = 4.15;
    document.querySelector('#angolo_tele_orizzontale').value = 5.2;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_lepton_50')
  {
    document.querySelector('#risoluzione_orizzontale').value = 60;
    document.querySelector('#angolo_tele_verticale').value = 25.5;
    document.querySelector('#angolo_tele_orizzontale').value = 18.75;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_lepton_25')
  {
    document.querySelector('#risoluzione_orizzontale').value = 60;
    document.querySelector('#angolo_tele_verticale').value = 12.5;
    document.querySelector('#angolo_tele_orizzontale').value = 9.37;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'flir_lepton3_50')
  {
    document.querySelector('#risoluzione_orizzontale').value = 120;
    document.querySelector('#angolo_tele_verticale').value = 28;
    document.querySelector('#angolo_tele_orizzontale').value = 21;
  }
  else if (document.querySelector('#telecamera option:checked').value == 'videotec_vcmhd30x01')
  {
    document.querySelector('#risoluzione_orizzontale').value = 1920;
    document.querySelector('#angolo_tele_verticale').value = 1.32;
    document.querySelector('#angolo_tele_orizzontale').value = 2.36;
  }

  if (aggiorna_dati)
      aggiorna_distanze ();
}

document.addEventListener('DOMContentLoaded', function() {

  els = document.querySelector('select[name="telecamera"] option[value="sony_fcb_ev7520"]');

  if(els)
  {
    els.selected = true;
  }

  aggiorna_telecamera (false);

  aggiorna_distanze ();

  document.getElementById('telecamera').addEventListener('change', function() {
      aggiorna_telecamera (true);
  });
}, false);

</script>
</div>

<h2 id="spiegazione-della-norma-eniec-62676-4">Spiegazione della norma EN/IEC 62676-4</h2>

<p>La norma EN/IEC 62676-4 raccoglie i requisiti e le raccomandazioni per la corretta progettazione, installazione, verifica e manutenzione di un Video Surveillance System (VSS).</p>

<p>Come tutte le normative più moderne, anche la 62676-4 è una norma basata sull’analisi dei rischi. L’analisi deve considerare:</p>

<ul>
  <li>il costo delle perdite</li>
  <li>l’ubicazione del sito</li>
  <li>le restrizioni di accesso al sito</li>
  <li>gli eventi pregressi</li>
</ul>

<p>L’output di tale analisi confluirà nel Documento dei Requisiti Operativi (DRO) e a ciascun elemento da proteggere verrà assegnato il grado di rischio, come definito nella EN/IEC 62676-1-1. Il grado di rischio può assumere i seguenti valori:</p>

<ul>
  <li><strong>1</strong>, ovvero <em>low risk</em>, che identifica un sistema o sito che non necessita del controllo delle funzioni perché non esposto al rischio di manomissioni</li>
  <li><strong>2</strong>, ovvero <em>low to medium risk</em>, che identifica un sistema o sito in cui non viene monitorato il funzionamento perché dispone di protezioni semplici contro le manomissioni. In un impianto di videosorveglianza per un obiettivo di questo tipo, questo si traduce nel fatto che le telecamere non sono accessibili dall’esterno.</li>
  <li><strong>3</strong>, ovvero <em>medium to high risk</em>, che identifica un sistema o sito in cui vengono controllati sia il funzionamento che le manomissioni. A questo livello di rischio corrisponde anche una preprazione tecnica di base dell’aggressore, che potrebbe sapere come manomettere il sistema di videosorveglianza.</li>
  <li><strong>4</strong>, ovvero <em>high risk</em>, che identifica un sistema o sito con elevate protezioni contro le manomissioni in cui viene garantito anche il controllo della funzionalità. A questo livello di rischio sono associate un’alta competenza tecnica degli intrusori e non è ammesso perdere le registrazioni.</li>
</ul>

<p>In base al grado rischio di ciascun elemento da proteggere potr&amp;agrave essere usata una telecamera per verificare il corretto funzionamento del bene. In base all’oggetto da proteggere, la camera potrà effettuare una delle azioni previste dal modello <strong>MDORII</strong>:</p>

<ul>
  <li><strong>M</strong>onitor che richiede 12 pixel/metro sul bersaglio</li>
  <li><strong>D</strong>etect che richiede 25 pixel/metro sul bersaglio</li>
  <li><strong>O</strong>bserve che richiede 62 pixel/metro sul bersaglio</li>
  <li><strong>R</strong>ecognise che richiede 125 pixel/metro sul bersaglio</li>
  <li><strong>I</strong>dentify che richiede 250 pixel/metro sul bersaglio</li>
  <li><strong>I</strong>nspect che richiede 1000 pixel/metro sul bersaglio</li>
</ul>

<p>Come accadeva nel criterio di Johnson, la massima distanza a cui una telecamera può garantire la risoluzione deciderata dipende dalla lente e dalle carettaristiche del sensore.</p>

<h2 id="un-po-di-formule">Un po’ di formule</h2>

<p>La distanza basata sulla EN/IEC 62676-4 è facilmente ricavabile con la trigonometria dei triangoli rettangoli.</p>

<p>\[ d = \frac{res}{2} . \frac{1}{pixel per metro} . \frac{1}{\tan( \alpha_{tele, hor}) } \]</p>

<p>Dove con α si indica l’angolo di visione, deifnito come nella <a href="../criterio-di-johnson">pagina del criterio di Johnson</a>.</p>]]></content><author><name></name></author><category term="articles" /><category term="cctv" /><summary type="html"><![CDATA[Il successore al criterio di Johnson per comparare i sistemi di videosorveglianza]]></summary></entry><entry><title type="html">Fine dell’eMBA14!</title><link href="https://www.campana.vi.it/blog/fine-dell-emba/" rel="alternate" type="text/html" title="Fine dell’eMBA14!" /><published>2018-07-19T00:00:00+00:00</published><updated>2018-07-19T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/fine-dell-emba</id><content type="html" xml:base="https://www.campana.vi.it/blog/fine-dell-emba/"><![CDATA[<p>Oggi è stato un gran giorno: dopo 20 mesi si è conclusa la quattordicesima edizione dell’Executive MBA del <a href="https://www.cuoa.it/">Cuoa Business School</a>!</p>

<p style="text-align: center; width:100%"><img src="/images/emba14.jpg" alt="Il lancio dei cappelli dell'eMBA14" /></p>

<p>Non è facile riassumere 20 mesi di studio, impegno e, inevitalmente, tensioni in una singola pagina di un blog. Le persone che ho conosciuto, le materie che ho studiato, la difficoltà nell’incastrare tutto questo con la famiglia, il lavoro e le trasferte, semplicemente non ci stanno.</p>

<p>Il giorno della proclamazione Giacomo Tomezzoli, un mio compagno di corso eletto a rappresentante della classe ha letto un discorso che riassume magnificamente l’esperienza. Poiché non voglio perdere questo discorso, ho deciso di riportarlo in questa pagina.</p>

<blockquote>
  <p>Buonasera al gradito ed egregio ospite dott. Sandro Stramare, al Presidente Visentin, al Direttore Scientifico Prof. Vinelli, ai rappresentanti della Faculty, ad Alessia e allo staff, a tutti i colleghi diplomandi, alla dott.ssa De Stefani che seguirà, a tutti voi presenti.</p>

  <p>Cari ingegneri e non ingegneri (perché se una cosa mi è parsa chiara da subito frequentando queste aule è che questa divisione in caste esiste, una iattura per gli ingegneri), è un onore per me rappresentare i diplomandi dell’EMBA 14 e poi capirete il motivo. Non preoccupatevi, nessuna presentazione ad effetti speciali oggi, ma qualche riflessione, qualche PERCHE’. Spero capace di dare una traccia di un viaggio, importante, che abbiamo fatto e che faremo.</p>

  <p style="text-align: center; width:100%"><img src="/images/discorso_emba14_1.png" alt="Una breve storia" /></p>

  <p>Per fare sintesi e farci un augurio, per raccontarci che cosa alla fine ci siamo portati a casa, ho deciso di recuperare ed adattare una storia che mi ha affascinato molto e che ritengo possa essere condivisa in questa occasione, magari qualcuno di voi l’ha già sentita. E’ una storia utilizzata nel talent management, è la storia di un ragazzino, un ragazzino che giocava a calcio per le strade e le piazze della sua città, fino ad arrivare ai campetti di periferia e poi sulla cima del mondo. La storia racconta che questo ragazzino tirasse con il piede destro, provasse – in sostanza - a fare tutto ciò che facevano i suoi coetanei. Fino al giorno in cui un signore, che lo osservava da bordo campo, gli disse: perché non provi con il piede sinistro? Scoprirai il tuo vero talento. E così fu, quel ragazzino diventò un giocatore, un campione, un fuoriclasse per l’esattezza, che arrivò a fare probabilmente il gol più bello nella storia del calcio moderno, con ben 11 tocchi di palla fatti col solo piede sinistro, il suo più autentico talento.</p>

  <p>Lasciamolo lì, per un momento, quel ragazzino, e proviamo ora a ripercorrere un’altra storia, brevemente, quella che ci ha cambiato, quella di questi 20 mesi intensi all’interno di questo magnifico contesto.</p>

  <p style="text-align: center; width:100%"><img src="/images/discorso_emba14_2.png" alt="Che cosa è stata la onstra storia" /></p>

  <p>Che cosa è stato l’EMBA? I nostri amici, parenti e colleghi se lo immaginano? Quando siamo arrivati non lo sapevamo cosa sarebbe diventato per noi. In queste aule, in una delle business school più antiche d’Italia tutto ha avuto corso come farebbe un torrente in piena.</p>

  <p>Partimmo con la contabilità d’azienda e fu subito terrorismo psicologico. Per non cadere nella trappola dell’effetto possessione la sera si ricalcavano gli appunti. Siamo passati a segmentare segmentare segmentare il mercato, per chiudere con un enciclopedico piano di marketing. Di lì a poco ci siamo cimentati con i metodi quantitativi e la matematica finanziaria, materia in cui la classe si è subito divisa in due gruppi: chi stava in apnea per più di 2 minuti e chi sapeva la matematica (io appartenevo al primo gruppo). Abbiamo costruito modelli su modelli di controllo di gestione nella calura dei mesi estivi – esattamente 12 mesi fa. Volato via il primo anno ecco arrivare la finanza, non le fiamme gialle, ma quella d’impresa. In equilibrio verticale ma precario per tutta la durata del corso abbiamo stabilito con accuratezza le opportunità finanziarie di una società biomedicale. Via di corsa quindi verso il modulo più lungo, le operation e la supply chian, dove siamo stati deliziati con addirittura una lezione aggiuntiva e dove abbiamo attraversato idealmente tutte le fasi della catana del valore. Siamo quindi passati al modulo delle risorse umane, dove imparammo a selezionare, organizzare e valutare… e dove siamo stati valutati (non facile come sembrava). Infine la strategia d’impresa, l’oceano blu, il posizionamento, i modelli di business, le fusioni e le acquisizioni. Il docente, avvisato dai colleghi, ci ha pregato di non essere anch’egli travolto dalla nostra tracotanza (vi prego poche paginette, ci ha detto).</p>

  <p>Ma fosse solo questo: un business game dominato dalla HairWash, un business plan combattuto fino alla fine dalla Froota e dalla Tonazzo, un project work dove tutti hanno cercato di stare con onore alle spalle della Hachiko team. Ma poi il project management, l’international management, l’information technology, le diversità culturali con gli abitanti dell’isola di Morenia (che qui non imito), le competenze trasversali, il publick speaking, la gestione dello stress, i lavori di gruppo bendati, quelli con le assi di legno (e qui sorvoliamo), con i ponti di carta, l’orientamento nei boschi di Tretto, la scrittura di un libro a 48 mani, la realizzazione di un gioco in scatola, Fred sull’iceberg che si stava sciogliendo, il beer game, gli incontri di coaching, la gestione della complessità. Basta, non c’era più spazio. Nemmeno Athena, dea greca della Sapienza avrebbe retto a tanto.</p>

  <p>Se è difficile spiegare COSA sia stato proviamo allora a raccontarvi COME è stato questo MBA? Non posso certo dirlo io, lo possono solo dire tutti loro, i miei colleghi.</p>

  <p style="text-align: center; width:100%"><img src="/images/discorso_emba14_3.png" alt="Come è stata la nostra storia" /></p>

  <p>Che cosa è stato l’EMBA? I nostri amici, parenti e colleghi se lo immaginano? Quando siamo arrivati non lo sapevamo cosa sarebbe diventato per noi. In queste aule, in una delle business school più antiche d’Italia tutto ha avuto corso come farebbe un torrente in piena.</p>
  <ol>
    <li>Acceleratore per Umberto ➡️ lavoratore instancabile</li>
    <li>Arricchente per Giacomo ➡️ fortunato e spero degno portavoce quest’oggi</li>
    <li>Autovalutazione per Enrico Z. ➡️ grande uomo di gruppo</li>
    <li>Coinvolgente per Marco ➡️ e i suoi puntuali e precisi punti di vista</li>
    <li>Complementativo per Graziano ➡️ pressione e passione nelle vene</li>
    <li>Conferma per Piazza ➡️ unico commerciale a cui interessa il viaggio oltre che la meta</li>
    <li>Confronto per Ketty ➡️ imprenditrice, linguista e ottimista</li>
    <li>Consapevolezza per Sabrina ➡️ super direttiva e affidabile</li>
    <li>Destabilizzante per Elena ➡️ visione strategica e decisione</li>
    <li>Edificante per Loris ➡️ alto potenziale, uno che vende armi al Vaticano</li>
    <li>Entusiasmante per Valentina ➡️ determinazione allo stato puro</li>
    <li>Fenomenale per Fabio ➡️ il pragmatismo e l’azione in persona</li>
    <li>Illuminante per Lorenzo ➡️ da cui la ormai famosa matrice di Lorenz</li>
    <li>Impegnativo per Riccardo B ➡️ uomo quadrato che quadra i conti, di tutti oserei dire</li>
    <li>Introspettivo per Paolo ➡️ il maestro, il sensei, venuto da Combai</li>
    <li>Neurostimolante per Soncin ➡️ manager veloce e sempre sul pezzo</li>
    <li>Pertinente per Giulia ➡️ preparata e consapevole non solo nello stile</li>
    <li>Prospettiva per Carlo ➡️ DNA da acuto osservatore</li>
    <li>Rete iperspaziale per Michele ➡️ loquace anticonformista</li>
    <li>Sicurezza per Ciuffo ➡️ sintesi perfetta di creatività ed ingegneria</li>
    <li>Stimolante per Sturli ➡️ professionista rigoroso e affidabile</li>
    <li>Stupefacente per Federico ➡️ il tecnico divenuto Manager</li>
    <li>Trasformante per Ottavio ➡️ ingegnere tutto d’un pezzo, però ora trasformato</li>
    <li>Variopinto per Manuel ➡️ il nostro saggio di classe, un sussidiario</li>
  </ol>

  <p>Non ci crederete ma 24 professionisti – che mi sono permesso di ricordare - mi hanno consegnato 24 definizioni diverse e bellissime, alle quali dedicherei un applauso. Un bel quadro che consegniamo al CUOA e che può essere usato per sponsorizzare le prossime edizioni dell’EMBA.</p>

  <p>Tuttavia queste belle definizioni non sono sufficienti. Perché ognuno ha sintetizzato all’estremo concetti e sentimenti. Perché ognuno è arrivato qui con le proprie aspettative, perché ognuno ha interpretato questo percorso come voleva, trascinato dagli altri, trascinando gli altri, ma anche indipendentemente dagli altri.</p>

  <p>Questa è stata una classe tosta, che dopo il primo giorno di presentazioni si è tolta subito di dosso i timori è ha iniziato a testa bassa ciò che si sarebbe dovuto fare. Una classe tosta, mi permetto di affermare, perché questo EMBA è stato tosto, nei contenuti, nelle relazioni, nelle dinamiche, nell’interfacciarsi con la faculty, che ha risposto con prontezza alle sollecitazioni, quando ricevute. C’è stato poco di scontato e tanto di guadagnato.</p>

  <p>Che cosa ci portiamo a casa? Possiamo tornare ora a quel ragazzino su quel campetto di periferia, archètipo di una storia che oggi ci appartiene più che mai.</p>

  <p style="text-align: center; width:100%"><img src="/images/discorso_emba14_4.png" alt="Cosa sarà la nostra storia" /></p>

  <p>Tutti noi siamo arrivati qui che sapevamo usare il nostro piede destro, anche bene direi: gli ingegneri di produzione sapevano fare gli ingegneri di produzione, i commerciali sapevano vendere, i tecnici sapevano fare i tecnici, i contabili sapevano che investimenti a lungo richiedono fonti a lungo, e così via. Siamo arrivati qui perché ci siamo resi conto che non volevamo più incaponirci solo con il nostro piede destro – questa è la verità - avevamo bisogno di coltivare nuovi talenti, perché solo con nuovi talenti potevamo puntare all’eccellenza. E così oggi chi sapeva gestire reparti di produzione sa anche di contabilità aziendale, chi gestiva una rete di fornitura sa anche comprendere le dinamiche del mercato, chi aveva cura dei clienti comprende anche le esigenze di un conto economico e di uno stabilimento produttivo, chi faceva marketing sa anche come è fatta un’impresa oltre che come sia fatto un sondaggio. Tutti hanno aggiunto talenti, hanno imparato ad usare meglio il loro piede destro, certamente, ma possono ambire a tirare anche con il loro piede sinistro, chissà forse fino ad arrivare un giorno anch’essi sulla cima del mondo.</p>

  <p>E allora cari colleghi, se è vero – come ci hanno detto - che l’innovazione è una disobbedienza andata a buon fine, noi disobbedimmo a noi stessi e alla prassi comune il giorno che siamo giunti qui e che abbiamo deciso di volerci impossessare di un po’ di innovazione, di qualche nuovo talento. E’ giusto che ora ci proviamo: chi riuscirà a segnare all’incrocio dei pali, chi colpirà la traversa, chi sfiorerà il goal, l’importante è che si continui ad entrare continuamente in contatto con il nostro piede sinistro, con i nostri nuovi talenti.</p>

  <p>Fatemi dire: i nostri figli nel 40% dei casi potrebbero fare un lavoro che oggi nemmeno esiste.</p>

  <p style="text-align: center; width:100%"><img src="/images/discorso_emba14_5.png" alt="I migliore manager mancini" /></p>

  <p>Vediamo se da qualcuno di questi FANTASTICI MANAGER MANCINI nascerà un’innovazione tanto importante.</p>

  <p>Lo possiamo fare per noi stessi, per le aziende e i clienti che crederanno in noi, e per le nostre famiglie, le persone che ci sono state vicine in questi quasi 20 mesi di fatiche. Anche loro come noi hanno vissuto momenti alterni, alti e bassi, entusiasmi e tensioni. Facciamogli allora un caloroso applauso…</p>

  <p>Grazie al Prof. Vinelli per la professionalità, ad Alessia e allo staff per quanto fatto, ai docenti tutti. Grazie ai miei colleghi ai quali rivolgo un invito: mai perdersi di vista!!!</p>

  <p style="text-align: center; width:100%"><img src="/images/discorso_emba14_6.png" alt="Stop wondering about wath's next" /></p>

  <p>e grazie a tutti voi per aver ascoltato un manager mancino.</p>

  <p>Giacomo Tomezzoli per gli EMBA 14.</p>
</blockquote>]]></content><author><name></name></author><category term="blog" /><category term="cv" /><category term="mba" /><category term="cuoa" /><summary type="html"><![CDATA[un altro ciclo di studio durato 20 mesi]]></summary></entry><entry><title type="html">Why time synchronization is important in CCTV</title><link href="https://www.campana.vi.it/articles/why-is-NTP-important-in-CCTV/" rel="alternate" type="text/html" title="Why time synchronization is important in CCTV" /><published>2018-06-17T00:00:00+00:00</published><updated>2018-06-17T00:00:00+00:00</updated><id>https://www.campana.vi.it/articles/why-is-NTP-important-in-CCTV</id><content type="html" xml:base="https://www.campana.vi.it/articles/why-is-NTP-important-in-CCTV/"><![CDATA[<p>I am writing this article, because once a season I get a complain about the need of time synchronization in CCTV systems. Installers want something that <em>just works</em>, no matter what the implication for the customer may be.</p>

<p>As many of you know, Videotec is contributing member of ONVIF, and I attend the Face2Face meetings and contribute to the ONVIF specifications. ONVIF is nowadays the standard protocol for the CCTV market, thus it is the best choice for explaining the issues that may rise with poor time synchronization between a camera and a VMS. These consideration in general apply to proprietary protocols as well, so even if you are not using ONVIF you should not stop reading this article.</p>

<h2 id="how-does-password-based-authentication-work-in-onvif">How does password-based authentication work in ONVIF?</h2>

<p>ONVIF is based on Web Services and the SOAP protocol. In the core specification, two methods are selected for authentication:</p>
<ul>
  <li>WS-UsernameToken</li>
  <li>HTTP digest</li>
</ul>

<p><a href="https://www.oasis-open.org/committees/download.php/13392/wss-v1.1-spec-pr-UsernameTokenProfile-01.htm">WS-UsernameToken</a> was the first authentication method for the ONVIF protocol. The credential is embedded in the header of the SOAP message used to transport.</p>

<p><a href="https://tools.ietf.org/html/rfc2617">HTTP digest</a> was added in a second phase of the protocol development. The main difference from the previous method is that it is based on the HTTP headers, thus it it not handled by the ONVIF protocol, but by the underlying web-server.</p>

<p>The <a href="https://www.onvif.org/specs/core/ONVIF-Core-Specification-v1712.pdf">ONVIF Core Specification</a> explains in Section 5.12.1 how to make these two authentication methods live together in an ONVIF device. They key idea is that an ONVIF device can support both methods, and wrong time synchronization impacts differently both of them.</p>

<h3 id="time-synchronization-in-ws-usernametoken">Time synchronization in WS-UsernameToken</h3>

<p>Let start by looking at an example of a payload of an authenticated SOAP message:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;wsse:Security&gt;
    &lt;wsse:UsernameToken&gt;
        &lt;wsse:Username&gt;NNK&lt;/wsse:Username&gt;
        &lt;wsse:Password Type="...#PasswordDigest"&gt;
            weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==
        &lt;/wsse:Password&gt;
        &lt;wsse:Nonce&gt;WScqanjCEAC4mQoBE07sAQ==&lt;/wsse:Nonce&gt;
        &lt;wsu:Created&gt;2003-07-16T01:24:32Z&lt;/wsu:Created&gt;
    &lt;/wsse:UsernameToken&gt;
&lt;/wsse:Security&gt;
</code></pre></div></div>

<p>As we can see, three elements are sent unencrypted:</p>
<ul>
  <li>the username</li>
  <li>the nonce</li>
  <li>the timestamp of the creation of the UsernameToken payload.</li>
</ul>

<p>The password is the output of an hashing function that takes into account not only the plain text password, but also the nonce and the timestamp, with the following formula:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
</code></pre></div></div>

<p>Since the camera stored internally the value of the password, it can recalculate the Password_Digest and only if it gets the same result as the one sent by the client it can authorize the required action.</p>

<p>Why does it hashes also the nonce and the timestamp? They are both needed to avoid <a href="https://en.wikipedia.org/wiki/Replay_attack">replay attacks</a>. If only the password is hashed, in practice there is no difference from using plain text password to access the service, as it happens in the <a href="https://tools.ietf.org/html/rfc7617">HTTP basic authentication schema</a>. In fact, if an attacker is able to get access to just one message, he could use the same password hash again and again to send malicious commands to the camera.</p>

<p>By adding a nonce and a time stamp, this risk is eliminated:</p>
<ul>
  <li>nonces shall not be accepted by the camera more than once. To implement this, cameras usually have a buffer of used nonces, and every time they have a new request they check if the supplied nonce has already been used.</li>
  <li>since cameras are embedded systems with limited RAM and storage, they cannot store all the previously used nonces. The buffer is usually sized so that the camera can hold all the nonce of an interval. Once a nonce is older that a set number of seconds, it is considered expired and removed from the list of used nonces. An attacker could at this point replay the nonce to attack the camera. By checking also the created timestamp the replay attack will fail, because repeating the nonce will generate a different hash value.</li>
</ul>

<p>As a final security measure, cameras verify that the created timestamp is synchronized with the internal clock with a certain clock skew tolerance. As an example, the default clock skew tolerance in gSoap is 300s. Any message generated with a difference of time bigger than the values is discarded because it could be either a replay attack or something trying to crack the camera’s firmware.</p>

<p><strong>Trivia</strong>: <em>how can an ONVIF tool configure a Profile-S camera that is just out from the box which implements only WS-UsernameToken as authentication method?</em></p>

<p>The correct handshake procedure is:</p>
<ol>
  <li>the client invokes <code class="language-plaintext highlighter-rouge">GetSystemDateAndTime</code> to get the camera clock value;</li>
  <li>the client checks its internal clock;</li>
  <li>the client determines the time difference between the two values;</li>
  <li>the client sends a <code class="language-plaintext highlighter-rouge">SetSystemDateAndTime</code> to synchronize the clock of the camera with the clock of the system;</li>
  <li>the client configures all other parameters.</li>
</ol>

<p>On the market there are VMSs that implement this handshake procedure when adding cameras into the system.</p>

<h3 id="time-synchronization-in-http-digest">Time synchronization in HTTP digest</h3>

<p>Unlike WS-UsernameToken, the <a href="https://tools.ietf.org/html/rfc7616">HTTP digest</a> authentication method does not explicitly include information about time in its definition. Still, in the RFC7616 the possibility of including the timestamp is suggested as an option to increase security.</p>

<p>Before entering the details for adding timestamp to the HTTP digest, it is necessary to explain how is works.</p>

<p>Let’s suppose that the client is requesting <code class="language-plaintext highlighter-rouge">/dir/index.html</code> page on a web-server</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /dir/index.html HTTP/1.0
Host: localhost
</code></pre></div></div>

<p>If the access to the page is restricted, the web-server will send back the following answer</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2014 20:26:47 GMT
WWW-Authenticate: Digest realm="testrealm@host.com",
                        qop="auth,auth-int",
                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                        opaque="5ccc069c403ebaf9f0171e9517f40e41"
Content-Type: text/html
Content-Length: 153

&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;title&gt;Error&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;401 Unauthorized.&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre></div></div>

<p>The key part of the response is the <code class="language-plaintext highlighter-rouge">WWW-Authenticate</code> header. This header specifies a realm, which is the description of the restricted area that the user is trying to access, the quality of protection <code class="language-plaintext highlighter-rouge">qop</code>, which specifies that authentication can be either be used to perform authentication (<code class="language-plaintext highlighter-rouge">auth</code>) or to to perform authentication and verification of message integrity (<code class="language-plaintext highlighter-rouge">auth-int</code>), the specifies a number that can be used only once every time a <code class="language-plaintext highlighter-rouge">WWW-Authenticate</code> header is generated called <code class="language-plaintext highlighter-rouge">nonce</code> and an <code class="language-plaintext highlighter-rouge">opaque</code> value, which is a string of that that should not be modified by the client.</p>

<p>At this point the client repeats the request, by adding the <code class="language-plaintext highlighter-rouge">Authorization</code> header:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
                     realm="testrealm@host.com",
                     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                     uri="/dir/index.html",
                     qop=auth,
                     nc=00000001,
                     cnonce="0a4f113b",
                     response="6629fae49393a05397450978507c4ef1",
                     opaque="5ccc069c403ebaf9f0171e9517f40e41"
</code></pre></div></div>

<p>As it can be easily seen, <code class="language-plaintext highlighter-rouge">realm</code>, <code class="language-plaintext highlighter-rouge">nonce</code> and <code class="language-plaintext highlighter-rouge">opaque</code> are sent back to the server without any modification, while the client chooses the <code class="language-plaintext highlighter-rouge">auth</code> method as quality of protection (which means only authentication)</p>

<p>Finally the web-server accepts the requests and gives a positive response:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP/1.0 200 OK
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:27:03 GMT
Content-Type: text/html
Content-Length: 7984
</code></pre></div></div>

<p>So far, so good, because time is not involved in the basic mechanism of the Digest authentication. However, this way of performing authentication is exposed to reply attacks. In fact, a client could re-transmit the same command to the camera with the unaltered <code class="language-plaintext highlighter-rouge">Authorization</code> field  and the camera could not discriminate if the sender is good or malicious. For this reason, RFC2617 states</p>

<blockquote>
  <p>The contents of the nonce are implementation dependent. The quality
of the implementation depends on a good choice. A nonce might, for
example, be constructed as the base 64 encoding of</p>

  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>time-stamp H(time-stamp ":" ETag ":" private-key)
</code></pre></div>  </div>

  <p>where time-stamp is a server-generated time or other non-repeating
value, ETag is the value of the HTTP ETag header associated with
the requested entity, and private-key is data known only to the
server.  With a nonce of this form a server would recalculate the
hash portion after receiving the client authentication header and
reject the request if it did not match the nonce from that header
or if the time-stamp value is not recent enough. In this way the
server can limit the time of the nonce’s validity.</p>
</blockquote>

<p>For this reason, devices that implement this strategy to prevent replay attacks must have a reliable source of time. Time must be monotone even after a reboot, to have this kind of protection working.</p>

<h2 id="what-about-encryption">What about encryption?</h2>

<p>Since password-based authentication schemes are in general weak, the ONVIF protocol offers a way to totally bypass passwords in the <a href="https://www.onvif.org/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl">Advanced Security specifications</a>.</p>

<p>In this schema, authentication and authorization are performed over x.509 certificates. However, certificates have a validity range, expressed in terms of Not Before and Not After. Thus, preventing the usage of expired certificates still requires time synchronization, to make sure certificates are accepted in the desired time interval.</p>

<h2 id="whats-the-best-timezone">What’s the best timezone?</h2>

<p>Many users require burning date and time into the video at compression time, so that they can easily recognize when the video was recorded. In order to get a better user experience, users required to have time expressed in their time zone.</p>

<p>This choice, unfortunately, may introduce drawbacks, because in may time zones time is not monotone. In fact, daylight savings introduces two discontinuities a year. In one part of the year local clocks will be advanced by one hour, leaving a hole of 60 minutes. In the other discontinuity, the same hour will exist twice, exposing the system to replay attacks.</p>

<p>Daylight saving is the most immediate use case scenario to understand that holes in time may exist, but it is not the only one. Let’s think about cargo ships: their journey is likely to span over different time zones.</p>

<h2 id="how-to-configure-an-authoritative-time-server-in-windows-server">How to configure an authoritative time server in Windows Server</h2>

<p>Windows offers an NTP server that can be used to synchronize the IP cameras. Unfortunately, in CCTV many systems are air-gapped, either because of security policies or simply because they are physically located in places where a connection to the Internet is not available.</p>

<p>If the Windows server is not able to synchronize with one or more server of a higher stratum, it will announce itself as a not authoritative NTP server. As a result, devices may refuse to synchronize with the server, preventing the CCTV from working.</p>

<p>Microsoft provides <a href="https://support.microsoft.com/en-us/help/816042/how-to-configure-an-authoritative-time-server-in-windows-server">instructions</a> about how to configure an isolated NTP server:</p>

<ol>
  <li>Select Start &gt; Run, type regedit, and then select OK.</li>
  <li>Locate and then select the following registry subkey: <code class="language-plaintext highlighter-rouge">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config\AnnounceFlags</code></li>
  <li>In the right-pane, right-click <code class="language-plaintext highlighter-rouge">AnnounceFlags</code>, and then select Modify.</li>
  <li>In Edit DWORD Value, type <code class="language-plaintext highlighter-rouge">A</code> in the Value data box, and then select OK.</li>
  <li>Close Registry Editor.</li>
  <li>At the command prompt, type the following command to restart the Windows Time service, and then press Enter: <code class="language-plaintext highlighter-rouge">net stop w32time &amp;&amp; net start w32time</code></li>
</ol>]]></content><author><name></name></author><category term="articles" /><category term="cctv" /><category term="onvif" /><category term="ntp" /><category term="security" /><category term="cybersecurity" /><category term="time" /><category term="synchronization" /><summary type="html"><![CDATA[and how you should fix your NTP server]]></summary></entry><entry><title type="html">Installare Armbian sulla scheda nanopi neo 2</title><link href="https://www.campana.vi.it/blog/installare-armbian-su-nanopi-neo-2/" rel="alternate" type="text/html" title="Installare Armbian sulla scheda nanopi neo 2" /><published>2018-03-04T00:00:00+00:00</published><updated>2018-03-04T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/installare-armbian-su-nanopi-neo-2</id><content type="html" xml:base="https://www.campana.vi.it/blog/installare-armbian-su-nanopi-neo-2/"><![CDATA[<p>Mi è stato richiesto di aiutare ad installare linux sulla scheda <a href="http://www.friendlyarm.com/index.php?route=product/product&amp;product_id=180">NanoPi neo 2</a> di cui non conoscevo l’esistenza. A dire il vero non conoscevo nemmeno il progetto <a href="https://www.armbian.com">Armbian</a>, così ho accettato di smanettarci su. Armbian è una distribuzione per schede embedded con processore ARM basata sui pacchetti di <a href="https://www.debian.org">Debian</a> e di <a href="https://www.ubuntu.com">Ubuntu</a>.</p>

<p>Per iniziare è necessario scaricare <a href="https://ethcer.io">Etcher</a>, il programma consigliato da Armbian per flashare le schede microSD. Il passo successivo consiste nello scaricare da Armbian l’<a href="https://www.armbian.com/nanopi-neo-2/">immagine del sistema operativo desiderato</a>. Ad oggi Armbian offre per la scheda in questione due immagini per server, una basata su Debian ed una basata su Ubuntu.</p>

<p>Al primo login ai accede con root/1234 ed il sistema chiede di cambiare la password. L’IP è in DHCP.</p>

<p>Essendo una Debian, la prima cosa che vien voglia di fare è smanettare con <code class="language-plaintext highlighter-rouge">apt-get</code>, ma per avere un sistema debina funzionante è necessario lanciare il comando  <code class="language-plaintext highlighter-rouge">update-command-not-found</code> per generare i file do configurazione di apt. Successivamente, come da ordinaria amministrazione, è necssario lanciare <code class="language-plaintext highlighter-rouge">apt-get update</code> aggiornare l’elenco dei pacchetti.</p>

<p>Il punto però che mi ha stupito di più è stato il fatto che in questa scheda è possibile installare sia pacchetti con archiettura <code class="language-plaintext highlighter-rouge">arm64</code> che con architettura <code class="language-plaintext highlighter-rouge">armhf</code>. GNU/Debian supporta il <a href="https://wiki.debian.org/Multiarch">multiarch</a>, è abbastanza comune la presenza di librerie x86 su sistemi amd64, ma è la prima volta che mi sono imbattuto nel multiarch su piattaforma ARM.</p>]]></content><author><name></name></author><category term="blog" /><category term="linux" /><category term="embedded" /><category term="armbian" /><summary type="html"><![CDATA[cosa è Armbian?]]></summary></entry><entry><title type="html">Come installare OpenProject su GNU/Debian Jessie</title><link href="https://www.campana.vi.it/blog/openproject-debian-jessie/" rel="alternate" type="text/html" title="Come installare OpenProject su GNU/Debian Jessie" /><published>2017-09-27T00:00:00+00:00</published><updated>2017-09-27T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/openproject-debian-jessie</id><content type="html" xml:base="https://www.campana.vi.it/blog/openproject-debian-jessie/"><![CDATA[<p>Sono un grande fan della suite <a href="https://www.atlassian.com/">Altassian</a>, soprattutto <a href="https://www.atlassian.com/software/jira">Jira</a>. Uso per alcuni miei progetti personali <a href="https://bitbucket.org/product">Bitbucket</a>, ma talvolta per altri progetti mi capita di aver bisogno di una soluzione simile alla piattaforma di Atlassian, ad un costo minore.</p>

<p>Dopo aver fatto diverse prove ho scoperto <a href="https://www.openproject.org">OpenProject</a>, che è meno stiloso di Jira ma è comunque molto completo. L’ho installato su un server baremetal di <a href="https://www.scaleway.com/">Scaleway</a>, riuscendo ad avere una piattaforma che posso condividere con i miei compagni di eMBA al prezzo di 3€/mese.</p>

<p>Le istruzioni online per installare OpenProject sono un po’ sgangherate, quindi ho deciso di riassumere qui tutti i passaggi, nel caso in cui altre persone fossero interessate.</p>

<p><strong>Una premessa</strong>: OpenProject è un programma opensource, che viene distribuito in pacchetti per chi lo vuole eseguire su un proprio server, altrimenti viene venduto <strong>as a service</strong>. I pacchetti per Debian sono disponibili ad oggi fino a Jessie, mentre per Stretch il software non è disponibile come già pacchettizzato e non è nemmeno agevole installarlo a mano, perché MySQL è diventato MariaDB e ci sono alcuni problemi con le versioni più aggiornate di Ruby. Ecco perché sul server di scaleway ho installato Debian Jessie e non Stretch. Per fortuna Debian fornisce gli aggiornamenti di sicurezza per oldstable, quindi non è un problema non avere l’ultima versione.</p>

<p>Iniziamo con l’importare la firma digitale del repository dei pacchetti di OpenProject per Debian Jessie:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# wget <span class="nt">-qO-</span> https://dl.packager.io/srv/opf/openproject-ce/key | apt-key add -
</code></pre></div></div>

<p>Per proseguire è necessario installare apt-transport-https, perché packager.io, il sito mediante il quale vengono distribuiti i pacchetti, fornisce il software solo mediante https e non http.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get <span class="nb">install </span>apt-transport-https
</code></pre></div></div>

<p>A questo punto è necessario aggiungere i repository per <a href="https://letsencrypt.org/">Let’s encrypt</a> ed OpenProject:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~#  <span class="nb">echo</span> <span class="s1">'deb http://ftp.debian.org/debian jessie-backports main'</span> | <span class="se">\</span>
    <span class="nb">tee</span> /etc/apt/sources.list.d/backports.list

root@debian:~# wget <span class="nt">-O</span> /etc/apt/sources.list.d/openproject-ce.list <span class="se">\</span>
    https://dl.packager.io/srv/opf/openproject-ce/stable/7/installer/debian/8.repo
</code></pre></div></div>

<p>Installiamo certbot per la generazione dei certificati</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get update
root@debian:~# apt-get <span class="nb">install </span>python-certbot-apache <span class="nt">-t</span> jessie-backports
root@debian:~# certbot <span class="nt">--apache</span> <span class="nt">-d</span> nome.sito.it
</code></pre></div></div>

<p>A questo punto, se la procedura è andata a buon fine, dopo aver risposto ad alcune semplici domande, dovreste vedere questi file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# <span class="nb">ls</span> /etc/letsencrypt/archive/nome.sito.it/
cert1.pem  chain1.pem  fullchain1.pem  privkey1.pem
root@debian:~#
</code></pre></div></div>

<p>I certificati di Let’s Encrypt hanno una durata limitata di 90 giorni e devono essere rinnovati. I motivi di questa scelta sono spiegati <a href="https://letsencrypt.org/2015/11/09/why-90-days.html">qui</a> e di fatto viene fatto perché si vuole fare in modo che se un certificato viene compromesso l’impatto sia limitato. Per non dover aggiornare a mano i certificati basta mettere una entry nella crontab:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# crontab <span class="nt">-e</span>
<span class="c"># m h  dom mon dow   command</span>
<span class="k">*</span> 7,19 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> certbot <span class="nt">-q</span> renew
</code></pre></div></div>

<p>e dimenticarsi del rinnovo dei certificati. Vale la pena notare che ad ogni rinnovo verranno generati dei vecchi certificati, con numero progressivo incrementato di uno. Quindi al primo rinnovo nella directory <code class="language-plaintext highlighter-rouge">/etc/letsencrypt/archive/nome.sito.it/</code> troveremo questi file</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# <span class="nb">ls</span> /etc/letsencrypt/archive/nome.sito.it/
cert1.pem  chain1.pem  fullchain1.pem  privkey1.pem
cert2.pem  chain2.pem  fullchain2.pem  privkey2.pem
root@debian:~#
</code></pre></div></div>

<p>Per evitare problemi al rinnovo, nei passaggi successivi verrà usata la directory <code class="language-plaintext highlighter-rouge">/etc/letsencrypt/live/nome.sito.it/</code>, che contiene link simbolici sempre aggiornati all’ultimo certificato generato.</p>

<p>Fatto questo è il momento di installare OpenProject:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get <span class="nb">install </span>openproject
</code></pre></div></div>

<p>Questo comando provvederà ad installare tutte le dipendenze. Per la creazione del sito bisogna lanciare il seguente comando e prepararsi a rispondere ad una serie di domande:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# openproject configure
</code></pre></div></div>

<p>Il programma di configurazione è molto ricco, io di solito scelgo di:</p>
<ul>
  <li>installare mysql</li>
  <li>installare apache2, che chiede il fully qualified domain name (FQDN) del sito, in questo caso <em>nome.sito.it</em> , non specifico nessun server path prefix perché ho un server ad hoc per usare OpenProject ed abilito ssl</li>
  <li>come file del certificato ssl imposto <code class="language-plaintext highlighter-rouge">/etc/letsencrypt/live/nome.sito.it/cert.pem</code></li>
  <li>come chiave del certificato imposto <code class="language-plaintext highlighter-rouge">/etc/letsencrypt/live/nome.sito.it/privkey.pem</code> come chiave ,</li>
  <li>non aggiungo alcuni CA bundle, perché in Debian c’è già il file <code class="language-plaintext highlighter-rouge">/etc/ssl/certs/ISRG_Root_X1.pem</code> , che è il certificato di Internet Security Research Group, ovvero la fondazione a capo di Let’s Encrypt</li>
  <li>installare svn</li>
  <li>installare git</li>
  <li>installare sendmail</li>
  <li>installare memcached</li>
</ul>

<p>A questo punto potrebbe essere rimasto attivo un disto di default di apache2, quindi per sicurezza basta lanciare questi due comandi</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# a2dissite 000-default-le-ssl.conf
root@debian:~# /etc/init.d/apache2 restart
</code></pre></div></div>

<p>A questo punto, per iniziare ad usare il programma, basta aprire un browser e visitare il sito https://nome.sito.it . Il primo accesso avverrà come admin/admin, ma verrà immediatamente richiesto di impostare un’altra password.</p>

<h2 id="controlliamo-di-non-aver-lasciato-schifezze-in-giro">Controlliamo di non aver lasciato schifezze in giro</h2>

<p>Il sito funziona, ma verifichiamo di non aver dimenticato in giro programmi indesiderati</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get <span class="nb">install </span>nmap
</code></pre></div></div>

<p>Cosa possiamo esserci dimenticati?</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# namp &lt;ip pubblico&gt;
Starting Nmap 6.47 <span class="o">(</span> http://nmap.org <span class="o">)</span> at 2017-09-27 14:36 CEST
Nmap scan report <span class="k">for </span>aa.bb.cc.dd
Host is up <span class="o">(</span>0.000011s latency<span class="o">)</span><span class="nb">.</span>
Not shown: 995 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh
25/tcp  open  smtp
80/tcp  open  http
111/tcp open  rpcbind
443/tcp open  https

Nmap <span class="k">done</span>: 1 IP address <span class="o">(</span>1 host up<span class="o">)</span> scanned <span class="k">in </span>2.57 seconds
root@debian:~# 
</code></pre></div></div>

<p>La porta 22 aperta va bene, è per ssh, altrimenti come facciamo manutenzione?</p>

<p>La porta 25 aperta è di postfix, e non va assolutamente bene. È vero che nel file <code class="language-plaintext highlighter-rouge">/etc/postfix/main.cf</code> c’è la riga:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
</code></pre></div></div>

<p>che non permette di ricevere email se non dall’interfaccia di loopback, tuttavia esistono delle RBL che mettono in lista nera i server che hanno la porta 25 aperta. Per ovviare questo è necessario pubblicare dei recordi SPF e DKIM nella zona del proprio dominio. La cosa più semplice per uso hobbistico invece è modificare <code class="language-plaintext highlighter-rouge">/etc/postfix/main.cf</code> sostituendo la riga</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>inet_interfaces = all
</code></pre></div></div>

<p>con</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>inet_interfaces = loopback-only
</code></pre></div></div>

<p>e riavviare postfix. L’ultima porta da sistemare è la 111, occupata dal processo rcpbind. Questo è dovuto al fatto che Debian installa portmap di default, e per rimuoverlo basta dare il seguente comando:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# dpkg <span class="nt">--purge</span> rpcbind nfs-common
</code></pre></div></div>

<h2 id="aggiornamento">Aggiornamento</h2>

<p>È stata rilasciata la versione 7.3.0 di OpenProject. La procedura di aggiornamento è semplice, ma richede un minimo di attenzione. Servono i seguenti comandi:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get update
root@debian:~# apt-get <span class="nb">install</span> <span class="nt">--only-upgrade</span> openproject
root@debian:~# openproject configure
</code></pre></div></div>

<p>In questo modo viene scompattato il pacchetto nuovo, ma per aggiornare il software viene usato il comando <code class="language-plaintext highlighter-rouge">openproject configure</code> che provvedere ad applicare tutte le migrazioni del database, se necessario.</p>

<p>Nella versione 7.3.0 di fine Settembre 2017 è stata introdotta la possibilità di citare gli utenti con <code class="language-plaintext highlighter-rouge">@nome</code> come nella suite Atlassian. Per fine Novembre 2017 è prevista la versione 7.4 che avrà altre evoluzioni minori, mentre per Marzo 2018 è previsto il rilascio di OpenProject 8.0 con un editor WYSIWYG per il wiki.</p>

<p>Per chi fosse interessato è possibile fare riferimento alla <a href="https://community.openproject.com/projects/openproject/timelines/62">roadmap</a> ed alla <a href="https://community.openproject.com/projects/openproject/timelines/36">development timeline</a> del progetto.</p>]]></content><author><name></name></author><category term="blog" /><category term="linux" /><category term="development" /><summary type="html"><![CDATA[una alternativa opensource a Jira]]></summary></entry><entry><title type="html">Come avere la bash dentro Vim</title><link href="https://www.campana.vi.it/blog/bash-dentro-vim/" rel="alternate" type="text/html" title="Come avere la bash dentro Vim" /><published>2017-09-15T00:00:00+00:00</published><updated>2017-09-15T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/bash-dentro-vim</id><content type="html" xml:base="https://www.campana.vi.it/blog/bash-dentro-vim/"><![CDATA[<p>Vi è mai capitato di dover copiare più di una schermata da una shell dentro vim? Ecco, smanettare con copia ed incolla è un po’ frustrante</p>

<p>Per questo motivo ho cercato se è possibile eseguire una shell dentro vim, e mi sono imbatutto in <a href="https://vim.sourceforge.io/scripts/script.php?script_id=2771">ConqueTerm</a> che sembra fare proprio quello che voglio. Basta scaricare il file <a href="https://vim.sourceforge.io/scripts/download_script.php?src_id=16279">conqueterm_2.2.vmb</a> ed installarlo con Vim</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:open conqueterm_2.2.vmb
:so %
</code></pre></div></div>

<p>I file <code class="language-plaintext highlighter-rouge">.vmb</code> sono file Vimball, ovvero dei file con i plugin per vim e gli script per automatizzarne l’installazione. Quindi basta aprirli ed eseguirli, come mostrato nel blocco precedente.</p>

<p>A questo punto riavviamo vim e proviamo il comando <code class="language-plaintext highlighter-rouge">:ConqueTermVSplit bash</code>. Voilla:</p>

<p><img src="/images/conqueterm.png" alt="conqueterm" /></p>]]></content><author><name></name></author><category term="blog" /><category term="linux" /><category term="vim" /><category term="bash" /><summary type="html"><![CDATA[per i fanatici di Vim come me]]></summary></entry><entry><title type="html">Come installare dotnet su GNU/Debian Stretch</title><link href="https://www.campana.vi.it/blog/installare-dotnet-debian-stretch/" rel="alternate" type="text/html" title="Come installare dotnet su GNU/Debian Stretch" /><published>2017-09-12T00:00:00+00:00</published><updated>2017-09-12T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/installare-dotnet-debian-stretch</id><content type="html" xml:base="https://www.campana.vi.it/blog/installare-dotnet-debian-stretch/"><![CDATA[<p>Agli albori di Linux, Microsoft era vista come un nemico. Dopo anni di lotta, Microsoft ha iniziato ad essere pià aperta verso altri sistemi operativi, in primis verso Linux.</p>

<p>Questa estate Microsoft ha rilasciato il <a href="https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-net-core-2-0/">.Net Core 2.0</a>. Il linguaggio mi ha sempre incuriosito, però non mi sono mai trovato bene con <a href="http://www.mono-project.com/">Mono</a>, il compilatore opensource per C# perché non supporta tutte le librerie di sistema di .Net. Con la nuova release di .Net e l’annuncio in grande stile del supporto della piattaforma Linux ho deciso di fare una prova.</p>

<p>Si inizia con l’installazione di un po’ di pacchetti di supporto:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get update
root@debian:~# apt-get <span class="nb">install </span>curl libunwind8 gettext apt-transport-https
</code></pre></div></div>

<p>Successivamente è necessario aggiungere la chiave con cui Microsoft firma i propri pacchetti:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# curl https://packages.microsoft.com/keys/microsoft.asc | gpg <span class="nt">--dearmor</span> <span class="o">&gt;</span> /etc/apt/trusted.gpg.d/microsoft.gpg
</code></pre></div></div>

<p>Successivamente è necessario aggiunere l’archivio dei pacchetti per Debian Stretch:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# <span class="nb">echo</span> <span class="s2">"deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-stretch-prod stretch main"</span> <span class="o">&gt;</span> /etc/apt/sources.list.d/dotnetdev.list
</code></pre></div></div>

<p>A questo punto è possibile procedere con l’installazione dell’SDK .Net:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# apt-get update
root@debian:~# apt-get <span class="nb">install </span>dotnet-sdk-2.0.0
</code></pre></div></div>

<h2 id="facciamo-un-hello-world">Facciamo un hello world</h2>

<p>A questo punto, per verificare che tutto funzioni, creiamo una applicazione i nconsole che saluti il mondo:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user@debian:~<span class="nv">$ </span>dotnet new console <span class="nt">-o</span> hello
user@debian:~<span class="nv">$ </span><span class="nb">cd </span>hello
</code></pre></div></div>

<p>Come si può vedere il comando crea una seriel di file, tra cui <code class="language-plaintext highlighter-rouge">Program.cs</code> :</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">hello</span>
<span class="p">{</span>
    <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Per compilare ed eseguire il programma basta lanciare il seguente comando:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user@debian:~/hello<span class="nv">$ </span>dotnet run
Hello world!
user@debian:~/hello<span class="nv">$ </span>
</code></pre></div></div>

<p>Se si vuole solo eseguire il programma senza compilarlo è possibile usare il seguente comando:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user@debian:~/hello<span class="nv">$ </span>dotnet <span class="nb">exec </span>bin/Debug/netcoreapp2.0/hello.dll
Hello world!
user@debian:~/hello<span class="nv">$ </span>
</code></pre></div></div>

<h2 id="e-per-i-webservice">E per i webservice?</h2>

<p>Purtroppo nonostate il core di .Net sia stato portato su Linux, il framework .Net è ancora disponibile solo su Windows. Come riportato <a href="https://fizzylogic.nl/2016/07/19/using-wcf-in-combination-with-net-core-sdk/">qua</a>, esiste il pacchetto <a href="https://visualstudiogallery.msdn.microsoft.com/c3b3666e-a928-4136-9346-22e30c949c08">WCF connected service</a> che permette di realizzare client di web service usando solo il core .Net . Purtroppo il generatore delle classi per accedere ai servizi funziona solo sotto windows, ma poi il codice è compilabile anche da Linux. Il vero problema è che WCF connected service supporta solo un sottoinsieme del suo equivalente sotto windows, ed è carente per tutte le estensioni WS-*, il che vuol dire mancanza di supporto a meccanismi di autenticazione, WD-Addressing, e meccanismi di trasporto.</p>

<p>Peccato, speravo meglio. Senza libreria grafica e funzioni di rete standard, C# su Linux rimane secondo me ancora un giocattolo.</p>]]></content><author><name></name></author><category term="blog" /><category term="linux" /><category term="c#" /><category term="dotnet" /><category term="development" /><summary type="html"><![CDATA[Un piccolo esperimento, visto che tutto il core è stato portato su GNU/Linux]]></summary></entry><entry><title type="html">Spiegazione dei parametri di u-boot</title><link href="https://www.campana.vi.it/blog/spiegazione-parametri-uboot/" rel="alternate" type="text/html" title="Spiegazione dei parametri di u-boot" /><published>2017-07-24T00:00:00+00:00</published><updated>2017-07-24T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/spiegazione-parametri-uboot</id><content type="html" xml:base="https://www.campana.vi.it/blog/spiegazione-parametri-uboot/"><![CDATA[<p>Nel post precedente è stata preparata una scheda microSD con due partizioni, una VFAT per avviare u-boot ed una EXT2 da usare come rootfs.</p>

<p>U-boot è stato configurato con il file <code class="language-plaintext highlighter-rouge">uEnv.txt</code> il cui contenuto può risultare abbastanza criptico per i non addetti ai lavori. Per capirne il significato è meglio partire dalla shell di u-boot.</p>

<p>All’avvio della scheda, u-boot scrive il seguente output sulla seriale:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>U-Boot SPL 2017.05-jumpnow (Jun 22 2017 - 19:16:50)
Trying to boot from MMC1
reading u-boot.img
reading u-boot.img


U-Boot 2017.05-jumpnow (Jun 22 2017 - 19:16:50 +0200)

CPU  : AM335X-GP rev 2.1
I2C:   ready
DRAM:  512 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Using default environment

&lt;ethaddr&gt; not set. Validating first E-fuse MAC
Net:   cpsw
Press SPACE to abort autoboot in 2 seconds
</code></pre></div></div>

<p>Se durante i due secondi viene premuta la barra spaziatrice il processo di boot si interrompe e u-boot propone la sua shell interattiva:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; 
</code></pre></div></div>

<p>U-boot ha una serie di parametri predefiniti, nel caso della beaglebone black questi parametri sono un numero molto elevato. Per vederli è necessario lanciare il comando <code class="language-plaintext highlighter-rouge">printenv</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; printenv
arch=arm
args_mmc=run finduuid;setenv bootargs console=${console} ${optargs} root=PARTUUID=${uuid} rw rootfstype=${mmcrootfstype}
baudrate=115200
board=am335x
board_name=A335BNLT
board_rev=00C0
board_serial=2716BBBK2133
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootarm.efi; if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
boot_fdt=try
boot_fit=0
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc0 legacy_mmc0 mmc1 legacy_mmc1 nand0 pxe dhcp
bootcmd=run envboot; setenv mmcdev 1; run envboot; setenv mmcdev 0; run findfdt; run distro_bootcmd
bootcmd_dhcp=if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00010:UNDI:003000;setenv bootp_arch 0xa;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_legacy_mmc0=setenv mmcdev 0; setenv bootpart 0:2 ; run mmcboot
bootcmd_legacy_mmc1=setenv mmcdev 1; setenv bootpart 1:2 ; run mmcboot
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_nand=run nandboot
bootcmd_pxe=dhcp; if pxe get; then pxe boot; fi
bootcount=2
bootdelay=2
bootdir=/boot
bootenvfile=uEnv.txt
bootfile=zImage
bootm_size=0x10000000
bootpart=0:2
bootscript=echo Running bootscript from mmc${mmcdev} ...; source ${loadaddr}
console=ttyO0,115200n8
cpu=armv7
dfu_alt_info_emmc=rawemmc raw 0 3751936
dfu_alt_info_mmc=boot part 0 1;rootfs part 0 2;MLO fat 0 1;MLO.raw raw 0x100 0x100;u-boot.img.raw raw 0x300 0x400;spl-os-args.raw raw 0x80 0x80;spl-os-image.raw raw 0x900 0x2000;spl-os-args fat 0 1;spl-os-image fat 0 1;u-boot.img fat 0 1;uEnv.txt fat 0 1
dfu_alt_info_ram=kernel ram 0x80200000 0x4000000;fdt ram 0x80f80000 0x80000;ramdisk ram 0x81000000 0x4000000
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
efi_dtb_prefixes=/ /dtb/ /dtb/current/
envboot=mmc dev ${mmcdev}; if mmc rescan; then echo SD/MMC found on device ${mmcdev};if run loadbootenv; then echo Loaded env from ${bootenvfile};run importbootenv;fi;if test -n $uenvcmd; then echo Running uenvcmd ...;run uenvcmd;fi;fi;
eth1addr=04:a3:16:b1:10:d7
ethact=cpsw
ethaddr=04:a3:16:b1:10:d5
fdt_addr_r=0x88000000
fdtaddr=0x88000000
fdtfile=undefined
findfdt=if test $board_name = A335BONE; then setenv fdtfile am335x-bone.dtb; fi; if test $board_name = A335BNLT; then setenv fdtfile am335x-boneblack.dtb; fi; if test $board_name = BBBW; then setenv fdtfile am335x-boneblack-wireless.dtb; fi; if test $board_name = BBG1; then setenv fdtfile am335x-bonegreen.dtb; fi; if test $board_name = BBGW; then setenv fdtfile am335x-bonegreen-wireless.dtb; fi; if test $board_name = BBBL; then setenv fdtfile am335x-boneblue.dtb; fi; if test $board_name = A33515BB; then setenv fdtfile am335x-evm.dtb; fi; if test $board_name = A335X_SK; then setenv fdtfile am335x-evmsk.dtb; fi; if test $board_name = A335_ICE; then setenv fdtfile am335x-icev2.dtb; fi; if test $fdtfile = undefined; then echo WARNING: Could not determine device tree to use; fi;
finduuid=part uuid mmc ${bootpart} uuid
fit_bootfile=fitImage
fit_loadaddr=0x88000000
importbootenv=echo Importing environment from mmc${mmcdev} ...; env import -t ${loadaddr} ${filesize}
init_console=if test $board_name = A335_ICE; then setenv console ttyO3,115200n8;else setenv console ttyO0,115200n8;fi;
kernel_addr_r=0x82000000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
loadaddr=0x82000000
loadbootenv=fatload mmc ${mmcdev} ${loadaddr} ${bootenvfile}
loadbootscript=load mmc ${mmcdev} ${loadaddr} boot.scr
loadfdt=load ${devtype} ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}
loadfit=run args_mmc; bootm ${loadaddr}#${fdtfile};
loadimage=load ${devtype} ${bootpart} ${loadaddr} ${bootdir}/${bootfile}
loadramdisk=load mmc ${mmcdev} ${rdaddr} ramdisk.gz
mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi
mmcboot=mmc dev ${mmcdev}; setenv devnum ${mmcdev}; setenv devtype mmc; if mmc rescan; then echo SD/MMC found on device ${mmcdev};if run loadimage; then if test ${boot_fit} -eq 1; then run loadfit; else run mmcloados;fi;fi;fi;
mmcdev=0
mmcloados=run args_mmc; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdtaddr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
mmcrootfstype=ext4 rootwait
netargs=setenv bootargs console=${console} ${optargs} root=/dev/nfs nfsroot=${serverip}:${rootpath},${nfsopts} rw ip=dhcp
netboot=echo Booting from network ...; setenv autoload no; dhcp; run netloadimage; run netloadfdt; run netargs; bootz ${loadaddr} - ${fdtaddr}
netloadfdt=tftp ${fdtaddr} ${fdtfile}
netloadimage=tftp ${loadaddr} ${bootfile}
nfsopts=nolock
partitions=uuid_disk=${uuid_gpt_disk};name=rootfs,start=2MiB,size=-,uuid=${uuid_gpt_rootfs}
pxefile_addr_r=0x80100000
ramargs=setenv bootargs console=${console} ${optargs} root=${ramroot} rootfstype=${ramrootfstype}
ramboot=echo Booting from ramdisk ...; run ramargs; bootz ${loadaddr} ${rdaddr} ${fdtaddr}
ramdisk_addr_r=0x88080000
ramroot=/dev/ram0 rw
ramrootfstype=ext2
rdaddr=0x88080000
rootpath=/export/rootfs
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootarm.efi; then echo Found EFI removable media binary efi/boot/bootarm.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x80000000
soc=am33xx
spiargs=setenv bootargs console=${console} ${optargs} root=${spiroot} rootfstype=${spirootfstype}
spiboot=echo Booting from spi ...; run spiargs; sf probe ${spibusno}:0; sf read ${loadaddr} ${spisrcaddr} ${spiimgsize}; bootz ${loadaddr}
spibusno=0
spiimgsize=0x362000
spiroot=/dev/mtdblock4 rw
spirootfstype=jffs2
spisrcaddr=0xe0000
static_ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off
stderr=ns16550_serial
stdin=ns16550_serial
stdout=ns16550_serial
update_to_fit=setenv loadaddr ${fit_loadaddr}; setenv bootfile ${fit_bootfile}
vendor=ti
ver=U-Boot 2017.05-jumpnow (Jun 22 2017 - 19:16:50 +0200)

Environment size: 8250/131068 bytes
=&gt;
</code></pre></div></div>

<p>Di tutti questi parametri ce ne sono alcuni che sono necessari per effettuare il boot del sistema compilato in precedenza, altri che servono per effettuare il boot di sistemi linux da altri dispositivi. In particolar modo i parametri più importanti sono:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">bootdelay=2</code> è il numero di secondi che il bootloader attende prima di lanciare il comando di boot del sistema linux. Può essere utile aumentare il valore di questa variabile per dare un po’ più di tempo all’operatore per interrompere il boot del sistema. Nel caso di prodotti finiti invece non è raro trovare sistemi configurati con <code class="language-plaintext highlighter-rouge">bootdelay=0</code>, che impedisce di lanciare una shell interattiva e fa subito lanciare il boot di sistema. Questo assieme ad altri accorgimenti viene usato per ridurre il tempo di boot.</li>
  <li><code class="language-plaintext highlighter-rouge">bootpart=0:2</code> indica la partizione da cui effettuare il boot. E’ della sintassi <code class="language-plaintext highlighter-rouge">x:y</code>, dove <code class="language-plaintext highlighter-rouge">x</code> indica il numero della microSD ed <code class="language-plaintext highlighter-rouge">y</code> è il numero della partizione. <code class="language-plaintext highlighter-rouge">x</code> vale 0 per indicare la microSD da cui è stato avviato u-boot, quindi nel caso in cui venga fatto il boot da microSD il valore 0 indica la microSD ed il valore 1 la eMMC, mentre se si avvia il sistema preinstallato nella memoria eMMC, ad <code class="language-plaintext highlighter-rouge">x=0</code> corrisponde la eMMC e a <code class="language-plaintext highlighter-rouge">x=1</code> corrisponde la microSD. Il valore di <code class="language-plaintext highlighter-rouge">y</code> indica invece l’indice della partizione dove cercare il kernel, che nel nostro caso è stato messo nella prima partizione VFAT, assieme ad MLO e u-boot.</li>
  <li><code class="language-plaintext highlighter-rouge">bootdir=/boot</code> indica se il kernel deve essere cercato in una subdirectory o nella radice del filesystem. Nei sistemi GNU/Linux desktop tradizionalmente kernel e di file necessari all’avvio sono salvati nella cartella <code class="language-plaintext highlighter-rouge">/boot</code>, mentre nei sistemi embedded può essere vero o meno. Spesso la scelta di dove mettere kernel e bootloader è più dettata dalle strategie di firmware upgrade che motivi estetici.</li>
  <li><code class="language-plaintext highlighter-rouge">console=ttyO0,115200n8</code> è uno dei parametri fondamentali: informa il kernel di redirigere il suo output della seriale. Solo in questo modo è possibile vedere come si comporta al boot e determinare quali sono i problemi che fanno fallire il boot del sistema. Per non sbagliare lo passo sempre, perché passarlo non costa nulla e non si sa mai come sia configurato un sistema. Non è questa variabile ad essere la diretta responsabile della redirezione dell’output del kernel sulla seriale, ma viene usata per formare i <code class="language-plaintext highlighter-rouge">bootargs</code>, ovvero la linea di comando che viene passata al kernel.</li>
  <li><code class="language-plaintext highlighter-rouge">fdtaddr=0x88000000</code> specifica a quale indirizzo caricare in memoria il <a href="https://www.devicetree.org/">Flattened Device Tree</a> (FDT), ovvero il file che indica come mappare tutte le periferiche del System-on-Chip.</li>
  <li><code class="language-plaintext highlighter-rouge">fdtfile=undefined</code> specifica il nome di quale FDT utilizzare.</li>
  <li><code class="language-plaintext highlighter-rouge">loadaddr=0x82000000</code> specifica invece a quale indirizzo di memoria caricare il kernel.</li>
  <li><code class="language-plaintext highlighter-rouge">mmcroot=/dev/mmcblk0p2 ro</code> questa variabile viene usata per assemblare i <code class="language-plaintext highlighter-rouge">bootargs</code>, per indicare al kernel di linux dove troverà il rootfs.</li>
  <li><code class="language-plaintext highlighter-rouge">mmcrootfstype=ext2 rootwait</code> anche questa varaibile viene usate per assemblare i <code class="language-plaintext highlighter-rouge">bootargs</code>, nello specifico per dire al kernel di linux che tipo di filesystem deve aspettarsi per il rootfs.</li>
  <li><code class="language-plaintext highlighter-rouge">optargs=consoleblank=0</code> è una variabile che, sempre usata per assemblare i <code class="language-plaintext highlighter-rouge">bootargs</code>, dice al kernel di non mettere in risparmio energetico un eventuale monitor collegato alla porta HDMI della scheda.</li>
  <li><code class="language-plaintext highlighter-rouge">nohdmi=bbb-nohdmi.dtb</code> indica il file contenente il Device Tree Database</li>
  <li><code class="language-plaintext highlighter-rouge">loadimage=load ${devtype} ${bootpart} ${loadaddr} ${bootdir}/${bootfile}</code> è il comando con cui u-boot carica il kernel.</li>
</ul>

<p>Quando u-boot leggere il file <code class="language-plaintext highlighter-rouge">uEnv.txt</code> legge tutti i parametri impostati dall’utente e sovrascrive i valori di default. Nello specifico, vediamo cosa cambia con le variabili passate mediante il file <code class="language-plaintext highlighter-rouge">uEnv.txt</code> . Per semplificare la cosa le righe sono state riordinate</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bootpart=0:1
bootdir=
bootfile=zImage
loadimage=load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile}
</code></pre></div></div>
<p>Queste prime righe dicono di selezionare la prima partizione della scheda microSD e di caricare il file <code class="language-plaintext highlighter-rouge">zImage</code> direttamente dalla radice del filesystem.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>console=ttyO0,115200n8
mmcroot=/dev/mmcblk0p2 ro
mmcrootfstype=ext2 rootwait
optargs=consoleblank=0
mmcargs=setenv bootargs console=${console} ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype}
</code></pre></div></div>

<p>Di queste righe, le prime quattro vengono usate per specificare dei parametri usati per assemblare i <code class="language-plaintext highlighter-rouge">bootargs</code>. L’ultima riga definisce il comando <code class="language-plaintext highlighter-rouge">mmcargs</code>, che quando viene eseguito popola la variabile d’ambiente d’ambiente <code class="language-plaintext highlighter-rouge">bootargs</code> con tutti i parametri da passare al kernel.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fdtaddr=0x88000000
fdtfile=zImage-bbb-nohdmi.dtb
loadaddr=0x82000000
nohdmi=bbb-nohdmi.dtb
findfdtfile=if test -e mmc ${bootpart} ${bootdir}/nohdmi; then setenv fdtfile ${nohdmi}; fi;
loadfdt=run findfdtfile; load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}
</code></pre></div></div>

<p>Queste variabili ridefiniscono alcuni parametri necessari al caricamento del Device Tree Database da usare con la scheda</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>uenvcmd=if run loadfdt; then echo Loaded ${fdtfile}; if run loadimage; then run mmcargs; bootz ${loadaddr} - ${fdtaddr}; fi; fi;
</code></pre></div></div>

<p>Questa ultima riga definisce il comando predefinito che u-boot esegue quando spira il timeout <code class="language-plaintext highlighter-rouge">bootdelay</code>. Nello specifico, il comando esegue i seguenti passi:</p>

<ol>
  <li>Carica il device tree database</li>
  <li>Carica il kernel</li>
  <li>Esegue <code class="language-plaintext highlighter-rouge">mmcargs</code> per impostare i <code class="language-plaintext highlighter-rouge">bootargs</code></li>
  <li>Lancia il kernel</li>
</ol>

<h2 id="trivia">Trivia</h2>

<p>Dopo aver letto questa pagina dovrebbe essere chiaro che i <code class="language-plaintext highlighter-rouge">bootargs</code> giocano un ruolo fondamentale nel boot, perché cambiano non poco il comportamento del kernel. Talvolta per debug serve capire con quali bootargs il sistema sia partito avendo solo a disposizione solo lo GNU/Linux già funzionante.</p>

<p>Come si può vedere con quali <code class="language-plaintext highlighter-rouge">bootargs</code> è partito?</p>

<p>La risposta è leggendo <code class="language-plaintext highlighter-rouge">/proc/cmdline</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@beaglebone:~# <span class="nb">cat</span> /proc/cmdline
<span class="nv">console</span><span class="o">=</span>ttyO0,115200n8 <span class="nv">consoleblank</span><span class="o">=</span>0 <span class="nv">root</span><span class="o">=</span>/dev/mmcblk0p2 ro <span class="nv">rootfstype</span><span class="o">=</span>ext2 rootwait
root@beaglebone:~#
</code></pre></div></div>]]></content><author><name></name></author><category term="blog" /><summary type="html"><![CDATA[uEnv.txt contiene molti parametri, ma cosa fanno di preciso?]]></summary></entry><entry><title type="html">Come funziona il boot della beaglebone black</title><link href="https://www.campana.vi.it/blog/boot-beaglebone-black/" rel="alternate" type="text/html" title="Come funziona il boot della beaglebone black" /><published>2017-07-03T00:00:00+00:00</published><updated>2017-07-03T00:00:00+00:00</updated><id>https://www.campana.vi.it/blog/boot-beaglebone-black</id><content type="html" xml:base="https://www.campana.vi.it/blog/boot-beaglebone-black/"><![CDATA[<p>Al termine del post precedente la compilazione della <code class="language-plaintext highlighter-rouge">core-image-minimal</code> aveva generato svariati file, tra cui:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">MLO</code></li>
  <li><code class="language-plaintext highlighter-rouge">u-boot.img</code></li>
  <li><code class="language-plaintext highlighter-rouge">zImage</code></li>
  <li><code class="language-plaintext highlighter-rouge">zImage-bbb-nohdmi.dtb</code></li>
  <li><code class="language-plaintext highlighter-rouge">core-image-minimal-beaglebone.tar.xz</code></li>
</ul>

<p>Il processore della Beaglebone Black, un Texas Instruments Sitara AM3358 è un processore molto ricco e offre diverse modalità di boot. Il suo boot può essere suddiviso nelle seguenti fasi:</p>
<ol>
  <li>il processore esegue un microcodice fuso all’interno del System-on-Chip (SoC)  detto ROM Code o bootloader di primo livello, per cercare i dispositivi di boot</li>
  <li>il ROM code carica il bootloader di secondo livello o  x-loader, chiamato anche x-loader, SPL o MLO (si veda sotto perché ha così tanti nomi).</li>
  <li>MLO effettua una minima configurazione del sistema e carica u-boot</li>
  <li>u-boot inizializza altre periferiche, carica il kernel, predispone i suoi parametri di boot ed avvia il kernel</li>
  <li>il kernel completa l’inizializzazione dell’hardware, monta il rootfs e dà vita a init che porta a termine l’inizializzazione del sistema</li>
</ol>

<h2 id="rom-code">ROM code</h2>

<p>Questo piccolo programma è fuso all’interno del chip, non è modificabile, e viene eseguito automaticamente all’accensione del sistema.</p>

<p>Il ROM code ha due funzioni principali:</p>
<ol>
  <li>Configurare ed inizializzare correttamente le periferiche primarie:
    <ul>
      <li>inizializzare lo stack</li>
      <li>configurare il watchdog ad un intervallo di tre minuti</li>
      <li>configurare i PLL ed i clock di sistema</li>
    </ul>
  </li>
  <li>Preparare il dispositivo per il bootloader di secondo livello:
    <ul>
      <li>verificare i dispositivi da cui caricare il bootloder successivo</li>
      <li>copiare in RAM il codice del bootloader da eseguire</li>
    </ul>
  </li>
</ol>

<p>La lista dei dispositivi da utilizzare nella ricerca del bootloader di secondo livello è configurata mediante dei pin di strap-in, indicati nella documentazione tecnica del Sitara come <code class="language-plaintext highlighter-rouge">SYSBOOT[15:0]</code>.</p>

<p>Nel caso della beaglebone, il funzionamento di questi pin è illustrato nella pagina 6 degli <a href="https://github.com/CircuitCo/BeagleBone-Black/blob/master/BBB_SCH.pdf?raw=true">schematici</a>.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">SYSBOOT[15:5]</code></th>
      <th style="text-align: center"><code class="language-plaintext highlighter-rouge">SYSBOOT[4:0]</code></th>
      <th style="text-align: left">Sequenza di boot</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left"><em>fissi</em></td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">11100b</code></td>
      <td style="text-align: left">MMC1, MMC0, UART0, USB0</td>
    </tr>
    <tr>
      <td style="text-align: left"><em>fissi</em></td>
      <td style="text-align: center"><code class="language-plaintext highlighter-rouge">11000b</code></td>
      <td style="text-align: left">SPI0, MMC0, USB0, UART0</td>
    </tr>
  </tbody>
</table>

<p>Come si può vedere a pagina 8 degli schematici per la eMMC e a pagina 11 per la microSD, la eMMC è connessa ad MMC1 e la microSD è connessa a MMC0. Sempre a pagina 6 si può vedere come è connesso il bottone <code class="language-plaintext highlighter-rouge">uSD BOOT</code>. Quando viene premuto porta il bit <code class="language-plaintext highlighter-rouge">SYSBOOT[2]</code> a 0 e quindi viene eseguita la seconda sequenza di boot, che va a ricercare il bootloader di secondo livello nella MMC0, mentre se non viene premuto il bit <code class="language-plaintext highlighter-rouge">SYSBOOT[2]</code> varrà 1 e quindi verrà eseguita la prima sequenza di boot, ch darà priorità al boot ad eMMC rispetto a quello da microSD. Le beaglebone sono tutte in vendita con un sistema linux embedded già programmato nella eMMC, quindi a meno di non cancellare la eMMC, se il bottone <code class="language-plaintext highlighter-rouge">uSD BOOT</code> non viene premuto il sistema partirà sempre da eMMC. In questo caso, volendo avviare un sistema custom da microSD, sarà sempre necessario accendere la beaglebone black tenendo premuto il bottone di boot da microSD.</p>

<table>
  <tbody>
    <tr>
      <td><img src="/images/beaglebone-black-interfaces.jpg" alt="descrizione delle interfacce dell beaglebone black" /></td>
    </tr>
    <tr>
      <td>Il bottone <code class="language-plaintext highlighter-rouge">uSD BOOT</code> è il <code class="language-plaintext highlighter-rouge">boot button</code> in questa immagine.</td>
    </tr>
  </tbody>
</table>

<p>Nel caso di boot da microSD, il ROM code cerca la prima partizione formattata in FAT32 e carica il file MLO contenuto in essa. Il file MLO contiene il bootloader di secondo livello ed alcuni byte di intestazione che istruiscono il ROM code sulla dimensione del file e l’indirizzo di memoria dove questo programma deve essere caricato.</p>

<h2 id="x-loader-il-bootloader-di-secondo-livello-detto-anche-secondary-program-loader-spl">x-loader, il bootloader di secondo livello detto anche Secondary Program Loader (SPL)</h2>

<p>Per spiegare cosa è x-loader è necesario un piccolo preambolo:</p>

<blockquote>
  <p>Fino a qualche anno fa la compilazione di u-boot aveva come output un singolo file, u-boot.bin o u-boot.img che doveva essere caricato nelle schede come primo bootloader. L’immagine di u-boot è “grande”, che nel mondo embedded vuol dire che è oltre 200KB. Questo può essere un problema, perché al momento del caricamento del bootloader in RAM non è detto che la DDR sia inizializzata e funzionante. I processori di Texas Instruments, anche prima del Sitara, avevano della RAM statica (SRAM) al loro interno, che al boot viene usata per l’inizializzazione del SoC. Per questo motivo, quando Texas Instruments ha iniziato a produrre i SoC della linea OMAP ha preso una parte dei sorgenti di u-boot per realizzare x-loader, una versione minimale di u-boot, il cui scopo è inizializzare la DDR e caricare uno u-boot completo. L’idea di avere un bootloader che usasse la RAM statica interna dei SoC per inizializzare la DDR è stata successivamente ripresa anche da altri produttori, per cui i sorgenti di x-loader e di altri bootloader minimali sono stati unificati con u-boot e per avere un unico bootloader ed evitare una proliferazione di minibootloader realizzati di diversi produttori di SoC.</p>
</blockquote>

<p>I compiti di questo bootloader sono:</p>
<ol>
  <li>inizializzare il pin muxing del SoC</li>
  <li>inizializzare i clock e la memoria DDR</li>
  <li>caricare lo u-boot vero e proprio in memoria ed eseguirlo</li>
</ol>

<p>I processori Sitara sono molto ricchi di periferiche e sebbene abbiano un migliaio di pin non sono in grado di esportare tutte le periferiche sulla loro footprint. Per questo motivo ed anche perché alcune periferiche possono essere connesse a piedini diversi in base alle necessità di sbroglio, è necessario configurare correttamente il multiplexing delle interfacce. Questo viene realizzato configurando opportunamente dei registri chiamati <code class="language-plaintext highlighter-rouge">PINMUX</code>, ed una prima configurazione deve essere fatta dal bootloader, per poter eseguire correttamente u-boot.</p>

<p>Dopo aver inizializzato la RAM DDR, il bootloader di secondo livello cerca il file u-boot.img nella stessa partizione da cui è stato caricato. Una volta che lo ha caricato il bootloader di secondo livello termina facendo un salto senza ritorno all’entry point di u-boot.</p>

<h2 id="u-boot">U-boot</h2>

<p>A questo punto tutta la procedura di boot a basso livello è stata completata e si arriva alla shell di u-boot, abbastanza familiare per chi lavora con i sistemi embedded.</p>

<p>Lo scopo di u-boot è:</p>
<ol>
  <li>configurare le periferiche per caricare il kernel</li>
  <li>caricare il Device Tree Blob (DTB) file in memoria per istruire il kernel su come configurare le periferiche</li>
  <li>caricare il kernel in memoria</li>
  <li>configurare i bootargs, variabili d’ambiente con cui si configurano alcune opzioni del kernel</li>
  <li>terminare la propria esecuzione, saltando senza ritorno all’entry point del kernel in RAM</li>
</ol>

<h2 id="ultima-parte-del-processo-di-boot">Ultima parte del processo di boot</h2>

<p>Una volta avvia il kernel effettua l’inizializzazione del sistema:</p>
<ol>
  <li>decompressione e riposizionamento del kernel in RAM</li>
  <li>inizializzazione delle periferiche rimanenti</li>
  <li>inizializzazione di tutte le strutture dati necessarie al sistema operativo</li>
  <li>inizializzazione degli stack di rete</li>
  <li><code class="language-plaintext highlighter-rouge">fork()</code> ed esecuzione del processo di init</li>
</ol>

<p>All’esecuzione di <code class="language-plaintext highlighter-rouge">fork()</code> viene creato il processo con PID 1. Questo è il primo, ed in partenza unico, processo in userspace. Questo processo viene eseguito come root e deve inizializzare tutto lo userland, far partire processi di getty sui pseudoterminali o sulle seriali, montare i filesystem e gestire la rete. Se per qualche motivo questo processo termina il sistema crasha, di solito con questo messaggio:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Kernel panic - not syncing: Attempted to kill init!
</code></pre></div></div>

<h2 id="dopo-la-teoria-la-pratica">Dopo la teoria, la pratica</h2>

<p>Per prima cosa è necesario partizionare la scheda microSD. La configurazione minima richiede due partizioni:</p>
<ul>
  <li>una partizione FAT32 dove mettere almeno <code class="language-plaintext highlighter-rouge">MLO</code>, <code class="language-plaintext highlighter-rouge">u-boot.img</code> ed <code class="language-plaintext highlighter-rouge">uEnv.txt</code></li>
  <li>una partizione EXT2 dove estrarre l’immagine compilata precedentemente</li>
</ul>

<p>Nel mio computer la microSD viene mappata su /dev/sdd, nel vostro potrebbe essere un altro device file, in base ai dispositivi connessi al vostro PC.</p>

<p>Il partizionamento seguito per questo esempio è il seguente e va bene per delle prime prove, ma come vedremo in futuro è troppo limitato se dobbiamo gestire anche il software upgrade.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# fdisk /dev/sdd

Command <span class="o">(</span>m <span class="k">for </span><span class="nb">help</span><span class="o">)</span>: p

Disk /dev/sdd: 15.9 GB, 15931539456 bytes
255 heads, 63 sectors/track, 1936 cylinders, total 31116288 sectors
Units <span class="o">=</span> sectors of 1 <span class="k">*</span> 512 <span class="o">=</span> 512 bytes
Sector size <span class="o">(</span>logical/physical<span class="o">)</span>: 512 bytes / 512 bytes
I/O size <span class="o">(</span>minimum/optimal<span class="o">)</span>: 512 bytes / 512 bytes
Disk identifier: 0x00000000

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1   <span class="k">*</span>          63      257039      128488+   c  W95 FAT32 <span class="o">(</span>LBA<span class="o">)</span>
/dev/sdd2          257040    31116287    15429624   83  Linux

Command <span class="o">(</span>m <span class="k">for </span><span class="nb">help</span><span class="o">)</span>:
</code></pre></div></div>

<p>Quando formattiamo la prima partizione della microSD è meglio assicurarsi di usare come dimensione 16 per le File Allocation Table (FAT), mentre per la seconda possiamo usare inizialmente un filesystem ext2 standard:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@debian:~# mkfs.vfat <span class="nt">-F</span> 16 <span class="nt">-n</span> <span class="s2">"boot"</span> /dev/sdd1
root@debian:~# mkfs.ext2 /dev/sdd2
</code></pre></div></div>

<p>A questo punto copiamo all’interno della partizione FAT i seguenti file:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">MLO</code></li>
  <li><code class="language-plaintext highlighter-rouge">u-boot.img</code></li>
  <li><code class="language-plaintext highlighter-rouge">zImage</code></li>
  <li><code class="language-plaintext highlighter-rouge">zImage-bbb-nohdmi.dtb</code></li>
</ul>

<p>Sempre all’interno di questa partizione creiamo il file <code class="language-plaintext highlighter-rouge">uEnv.txt</code> con il seguente contenuto:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bootpart=0:1
bootdir=
bootfile=zImage
console=ttyO0,115200n8
fdtaddr=0x88000000
fdtfile=zImage-bbb-nohdmi.dtb
loadaddr=0x82000000
mmcroot=/dev/mmcblk0p2 ro
mmcrootfstype=ext2 rootwait
optargs=consoleblank=0
nohdmi=bbb-nohdmi.dtb
mmcargs=setenv bootargs console=${console} ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype}
findfdtfile=if test -e mmc ${bootpart} ${bootdir}/nohdmi; then setenv fdtfile ${nohdmi}; fi;
loadfdt=run findfdtfile; load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}
loadimage=load mmc ${bootpart} ${loadaddr} ${bootdir}/${bootfile}
uenvcmd=if run loadfdt; then echo Loaded ${fdtfile}; if run loadimage; then run mmcargs; bootz ${loadaddr} - ${fdtaddr}; fi; fi;

</code></pre></div></div>

<p>Il file <code class="language-plaintext highlighter-rouge">uEnv.txt</code> contiene la configurazione di u-boot. Se non è presente u-boot userà i parametri hard coded che gli sono stati passati durante la compilazione. La spiegazione dettagliata dei parametri presenti in questo file verrà esposta in un prossimo post. <strong>ATTENZIONE</strong>: onde evitare problemi vari di compatibilità con alcune versioni di uboot, il file <code class="language-plaintext highlighter-rouge">uEnv.txt</code> deve essere salvato in formato Unix e deve avere come ultima riga una riga vuota.</p>

<p>La preparazione della partizione ext2 è molto più semplice, perché basta copiare il file <code class="language-plaintext highlighter-rouge">core-image-minimal-beaglebone.tar.xz</code> dentro alla partizione montata, posizionarsi nella directory dove è il file e lanciare il comando <code class="language-plaintext highlighter-rouge">tar xJfv core-image-minimal-beaglebone.tar.xz</code> . Notate come file è compresso con <code class="language-plaintext highlighter-rouge">xz</code> e non con i classici <code class="language-plaintext highlighter-rouge">gzip</code> o <code class="language-plaintext highlighter-rouge">bzip2</code>, quindi il flag per decomprimere questo tipo di archivio è <code class="language-plaintext highlighter-rouge">J</code>.</p>

<p>Per debuggare il boot è necessario connettersi alla prima UART della beaglebone black. Per fare questo la soluzione migliore è usare un adattatore USB.</p>

<p>L’UART è esportata sul connettore J1 e per connettermi uso un cavo simile a <a target="_blank" href="https://www.amazon.it/gp/product/B00AU00BWC/ref=as_li_tl?ie=UTF8&amp;camp=3414&amp;creative=21718&amp;creativeASIN=B00AU00BWC&amp;linkCode=as2&amp;tag=ocampana07-21&amp;linkId=da5561f73542f8272375ba12b79b4776">questo</a><img src="//ir-it.amazon-adsystem.com/e/ir?t=ocampana07-21&amp;l=am2&amp;o=29&amp;a=B00AU00BWC" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. E’ un cavo che converte emula da un lato una seriale su USB e dall’altro esporta tutti i segnali con livelli logici TTL.</p>

<p>Per connettere il cavo alla scheda bisogna collegare</p>
<ul>
  <li>la massa al pin J1 (quello a destra nell’immagine)</li>
  <li>l’RX al pin 4</li>
  <li>il TX al pin 5</li>
</ul>

<p>come mostrato in figura:</p>

<p><img src="/images/beaglebone_black_uart.jpg" alt="come connettersi all'UART della beaglebone black" /></p>

<p>Dopo aver configurato tutto, è necessario alimentare la scheda ricordandosi di tener premuto il bottone per il boot da microSD e questo è il risultato:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>U-Boot SPL 2017.05-jumpnow (Jun 22 2017 - 19:16:50)
Trying to boot from MMC1
reading u-boot.img
reading u-boot.img


U-Boot 2017.05-jumpnow (Jun 22 2017 - 19:16:50 +0200)

CPU  : AM335X-GP rev 2.1
I2C:   ready
DRAM:  512 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Using default environment

&lt;ethaddr&gt; not set. Validating first E-fuse MAC
Net:   cpsw
Press SPACE to abort autoboot in 2 seconds
switch to partitions #0, OK
mmc0 is current device
SD/MMC found on device 0
reading uEnv.txt
934 bytes read in 4 ms (227.5 KiB/s)
Loaded env from uEnv.txt
Importing environment from mmc0 ...
Running uenvcmd ...
reading /zImage-bbb-nohdmi.dtb
32741 bytes read in 8 ms (3.9 MiB/s)
Loaded zImage-bbb-nohdmi.dtb
reading /zImage
4068696 bytes read in 259 ms (15 MiB/s)
## Flattened Device Tree blob at 88000000
   Booting using the fdt blob at 0x88000000
   Loading Device Tree to 8fff5000, end 8fffffe4 ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.9.33-jumpnow (oe-user@oe-host) (gcc version 6.3.0 (GCC) ) #1 Thu Jun 22 18:36:49 CEST 2017
[    0.000000] CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt:Machine model: TI AM335x BeagleBone Black
[    0.000000] cma: Reserved 16 MiB at 0x9f000000
[    0.000000] Memory policy: Data cache writeback
[    0.000000] CPU: All CPU(s) started in SVC mode.
[    0.000000] AM335X ES2.1 (sgx neon)
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 130048
[    0.000000] Kernel command line: console=ttyO0,115200n8 consoleblank=0 root=/dev/mmcblk0p2 ro rootfstype=ext2 rootwait
[    0.000000] PID hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Memory: 484508K/524288K available (6144K kernel code, 387K rwdata, 1972K rodata, 1024K init, 8008K bss, 23396K reserved, 16384K cma-reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xe0800000 - 0xff800000   ( 496 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xe0000000   ( 512 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0700000   (7136 kB)
[    0.000000]       .init : 0xc0a00000 - 0xc0b00000   (1024 kB)
[    0.000000]       .data : 0xc0b00000 - 0xc0b60e68   ( 388 kB)
[    0.000000]        .bss : 0xc0b60e68 - 0xc13330fc   (8009 kB)
[    0.000000] Running RCU self tests
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] IRQ: Found an INTC at 0xfa200000 (revision 5.0) with 128 interrupts
[    0.000000] OMAP clockevent source: timer2 at 24000000 Hz
[    0.000014] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 89478484971ns
[    0.000034] clocksource: timer1: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[    0.000074] OMAP clocksource: timer1 at 24000000 Hz
[    0.000356] clocksource_probe: no matching clocksources found
[    0.001076] Console: colour dummy device 80x30
[    0.001127] Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar
[    0.001135] ... MAX_LOCKDEP_SUBCLASSES:  8
[    0.001142] ... MAX_LOCK_DEPTH:          48
[    0.001148] ... MAX_LOCKDEP_KEYS:        8191
[    0.001154] ... CLASSHASH_SIZE:          4096
[    0.001160] ... MAX_LOCKDEP_ENTRIES:     32768
[    0.001167] ... MAX_LOCKDEP_CHAINS:      65536
[    0.001173] ... CHAINHASH_SIZE:          32768
[    0.001179]  memory used by lock dependency info: 5167 kB
[    0.001186]  per task-struct memory footprint: 1536 bytes
[    0.001215] Calibrating delay loop... 996.14 BogoMIPS (lpj=4980736)
[    0.078819] pid_max: default: 32768 minimum: 301
[    0.079076] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.079089] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.081275] CPU: Testing write buffer coherency: ok
[    0.082342] Setting up static identity map for 0x80100000 - 0x80100058
[    0.086928] devtmpfs: initialized
[    0.116562] VFP support v0.3: implementor 41 architecture 3 part 30 variant c rev 3
[    0.117334] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.117396] futex hash table entries: 256 (order: 1, 11264 bytes)
[    0.118768] pinctrl core: initialized pinctrl subsystem
[    0.122777] NET: Registered protocol family 16
[    0.129307] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.166837] omap_hwmod: debugss: _wait_target_disable failed
[    0.222289] cpuidle: using governor ladder
[    0.222313] cpuidle: using governor menu
[    0.246782] OMAP GPIO hardware version 0.1
[    0.284400] No ATAGs?
[    0.284427] hw-breakpoint: debug architecture 0x4 unsupported.
[    0.355483] edma 49000000.edma: TI EDMA DMA engine driver
[    0.360199] usbcore: registered new interface driver usbfs
[    0.360372] usbcore: registered new interface driver hub
[    0.360536] usbcore: registered new device driver usb
[    0.361382] omap_i2c 44e0b000.i2c: could not find pctldev for node /ocp/l4_wkup@44c00000/scm@210000/pinmux@800/pinmux_i2c0_pins, deferring probe
[    0.361482] omap_i2c 4802a000.i2c: could not find pctldev for node /ocp/l4_wkup@44c00000/scm@210000/pinmux@800/i2c1_pins, deferring probe
[    0.361553] omap_i2c 4819c000.i2c: could not find pctldev for node /ocp/l4_wkup@44c00000/scm@210000/pinmux@800/i2c2_pins, deferring probe
[    0.389731] clocksource: Switched to clocksource timer1
[    0.463762] VFS: Disk quotas dquot_6.6.0
[    0.463874] VFS: Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
[    0.512171] NET: Registered protocol family 2
[    0.513750] TCP established hash table entries: 4096 (order: 2, 16384 bytes)
[    0.513837] TCP bind hash table entries: 4096 (order: 5, 147456 bytes)
[    0.514990] TCP: Hash tables configured (established 4096 bind 4096)
[    0.515145] UDP hash table entries: 256 (order: 2, 20480 bytes)
[    0.515312] UDP-Lite hash table entries: 256 (order: 2, 20480 bytes)
[    0.516016] NET: Registered protocol family 1
[    0.517487] hw perfevents: enabled with armv7_cortex_a8 PMU driver, 5 counters available
[    0.529063] workingset: timestamp_bits=30 max_order=17 bucket_order=0
[    0.535578] io scheduler noop registered
[    0.535603] io scheduler deadline registered
[    0.535677] io scheduler cfq registered (default)
[    0.538519] pinctrl-single 44e10800.pinmux: 142 pins at pa f9e10800 size 568
[    0.544084] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[    0.550640] omap_uart 44e09000.serial: no wakeirq for uart0
[    0.551199] 44e09000.serial: ttyO0 at MMIO 0x44e09000 (irq = 158, base_baud = 3000000) is a OMAP UART0
[    1.123670] console [ttyO0] enabled
[    1.128984] omap_uart 481a8000.serial: no wakeirq for uart4
[    1.135176] 481a8000.serial: ttyO4 at MMIO 0x481a8000 (irq = 159, base_baud = 3000000) is a OMAP UART4
[    1.146618] omap_uart 481aa000.serial: no wakeirq for uart5
[    1.152791] 481aa000.serial: ttyO5 at MMIO 0x481aa000 (irq = 160, base_baud = 3000000) is a OMAP UART5
[    1.165757] omap_rng 48310000.rng: OMAP Random Number Generator ver. 20
[    1.211514] brd: module loaded
[    1.237434] loop: module loaded
[    1.242392] mtdoops: mtd device (mtddev=name/number) must be supplied
[    1.259824] libphy: Fixed MDIO Bus: probed
[    1.264613] CAN device driver interface
[    1.339817] davinci_mdio 4a101000.mdio: davinci mdio revision 1.6
[    1.346205] davinci_mdio 4a101000.mdio: detected phy mask fffffffe
[    1.355078] libphy: 4a101000.mdio: probed
[    1.359284] davinci_mdio 4a101000.mdio: phy[0]: device 4a101000.mdio:00, driver SMSC LAN8710/LAN8720
[    1.370132] cpsw 4a100000.ethernet: Detected MACID = 04:a3:16:b1:10:d5
[    1.380580] usbcore: registered new interface driver asix
[    1.386354] usbcore: registered new interface driver ax88179_178a
[    1.392876] usbcore: registered new interface driver cdc_ether
[    1.399119] usbcore: registered new interface driver smsc95xx
[    1.405253] usbcore: registered new interface driver net1080
[    1.411285] usbcore: registered new interface driver cdc_subset
[    1.417570] usbcore: registered new interface driver zaurus
[    1.423567] usbcore: registered new interface driver cdc_ncm
[    1.436582] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    1.443498] ehci-omap: OMAP-EHCI Host Controller driver
[    1.449325] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    1.456013] usbcore: registered new interface driver cdc_wdm
[    1.462147] usbcore: registered new interface driver usbtest
[    1.471019] 47401300.usb-phy supply vcc not found, using dummy regulator
[    1.486585] 47401b00.usb-phy supply vcc not found, using dummy regulator
[    1.498303] musb-hdrc musb-hdrc.1.auto: MUSB HDRC host driver
[    1.510878] musb-hdrc musb-hdrc.1.auto: new USB bus registered, assigned bus number 1
[    1.520929] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
[    1.528038] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.535626] usb usb1: Product: MUSB HDRC host driver
[    1.540830] usb usb1: Manufacturer: Linux 4.9.33-jumpnow musb-hcd
[    1.547197] usb usb1: SerialNumber: musb-hdrc.1.auto
[    1.556615] hub 1-0:1.0: USB hub found
[    1.561550] hub 1-0:1.0: 1 port detected
[    1.572957] mousedev: PS/2 mouse device common for all mice
[    1.584128] omap_rtc 44e3e000.rtc: rtc core: registered 44e3e000.rtc as rtc0
[    1.592181] i2c /dev entries driver
[    1.595900] Driver for 1-wire Dallas network protocol.
[    1.607206] omap_wdt: OMAP Watchdog Timer Rev 0x01: initial timeout 60 sec
[    1.617027] omap_hsmmc 48060000.mmc: Got CD GPIO
[    1.743311] ledtrig-cpu: registered to indicate activity on CPUs
[    1.750259] usbcore: registered new interface driver usbhid
[    1.756086] usbhid: USB HID core driver
[    1.761170] oprofile: using arm/armv7
[    1.765580] Initializing XFRM netlink socket
[    1.770263] NET: Registered protocol family 17
[    1.775105] NET: Registered protocol family 15
[    1.779787] can: controller area network core (rev 20120528 abi 9)
[    1.786382] NET: Registered protocol family 29
[    1.791060] can: raw protocol (rev 20120528)
[    1.795609] can: broadcast manager protocol (rev 20161123 t)
[    1.801861] Key type dns_resolver registered
[    1.806498] omap_voltage_late_init: Voltage driver support not added
[    1.813760] ThumbEE CPU extension supported.
[    1.867224] mmc0: host does not support reading read-only switch, assuming write-enable
[    1.881112] mmc0: new high speed SDHC card at address aaaa
[    1.892372] mmcblk0: mmc0:aaaa SL16G 14.8 GiB
[    1.899725] random: fast init done
[    1.909992]  mmcblk0: p1 p2
[    1.928880] tps65217 0-0024: TPS65217 ID 0xe version 1.2
[    1.935234] omap_i2c 44e0b000.i2c: bus 0 rev0.11 at 400 kHz
[    1.944317] omap_i2c 4802a000.i2c: bus 1 rev0.11 at 100 kHz
[    1.953101] omap_i2c 4819c000.i2c: bus 2 rev0.11 at 100 kHz
[    1.961135] omap_rtc 44e3e000.rtc: setting system clock to 2000-01-01 00:00:01 UTC (946684801)
[    1.977073] mmc1: new high speed MMC card at address 0001
[    1.987339] mmcblk1: mmc1:0001 S10004 3.56 GiB
[    1.993067] VFS: Mounted root (ext2 filesystem) readonly on device 179:2.
[    2.001612] mmcblk1boot0: mmc1:0001 S10004 partition 1 4.00 MiB
[    2.009038] mmcblk1boot1: mmc1:0001 S10004 partition 2 4.00 MiB
[    2.017376] devtmpfs: mounted
[    2.022191] Freeing unused kernel memory: 1024K (c0a00000 - c0b00000)
[    2.031244]  mmcblk1: p1 p2
INIT: version 2.88 booting
Starting udev
[    2.597955] udevd[732]: starting version 3.2.1
[    2.663815] udevd[733]: starting eudev-3.2.1
[    3.816587] EXT2-fs (mmcblk0p2): warning: mounting unchecked fs, running e2fsck is recommended
Populating dev cache
Thu Jun 22 17:12:57 UTC 2017
INIT: Entering runlevel: 5
Configuring network interfaces... [    5.129363] net eth0: initializing cpsw version 1.12 (0)
[    5.230533] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=4a101000.mdio:00, irq=-1)
udhcpc (v1.24.1) started
Sending discover...
Sending discover...
Sending discover...
No lease, forking to background
done.
Starting syslogd/klogd: done

Poky (Yocto Project Reference Distro) 2.3 beaglebone /dev/ttyO0

beaglebone login:
</code></pre></div></div>

<p>Eccoci quindi arrivati al termine del primo boot di Yocto Pyro per Beaglebone Black!</p>

<p>Notiamo alcuni punti importanti:</p>
<ul>
  <li>Il ROM code non emette alcun output</li>
  <li>All’avvio MLO emette il seguente output <code class="language-plaintext highlighter-rouge">U-Boot SPL 2017.05-jumpnow (Jun 22 2017 - 19:16:50)</code></li>
  <li>Dopo essere stato avviato da MLO, U-boot emette <code class="language-plaintext highlighter-rouge">U-Boot 2017.05-jumpnow (Jun 22 2017 - 19:16:50 +0200)</code></li>
  <li>All’avvio il kernel emette <code class="language-plaintext highlighter-rouge">Starting kernel</code></li>
  <li>Quando il kernel è avviato, lo userspace prende vita con l’avvio del processo di init, in concomitanza dell’output <code class="language-plaintext highlighter-rouge">INIT: Entering runlevel: 5</code></li>
  <li>Al termine del setup dello userspace viene avviato getty sulla seriale, che richiede il login.</li>
</ul>]]></content><author><name></name></author><category term="blog" /><category term="linux" /><category term="debian" /><category term="yocto" /><category term="beaglebone" /><category term="boot" /><summary type="html"><![CDATA[Come MLO, u-boot, kernel e rootfs contribuiscono al boot della scheda]]></summary></entry></feed>