JingleOS: An operating system to embedded devices with language-based protection



Documentos relacionados
Introdução à Linguagem Java

4 Estrutura do Sistema Operacional Kernel

Prof. Marcos Ribeiro Quinet de Andrade Universidade Federal Fluminense - UFF Pólo Universitário de Rio das Ostras - PURO

Linguagem de Programação Introdução a Linguagem Java

Esta dissertação apresentou duas abordagens para integração entre a linguagem Lua e o Common Language Runtime. O objetivo principal da integração foi

Aspectos de Segurança em Programação com Java

ESTUDO DE CASO WINDOWS VISTA

CAPÍTULO 7 NÍVEL DE LINGUAGEM DE MONTAGEM

Fundamentos de Java. Prof. Marcelo Cohen. 1. Histórico

SISTEMAS OPERACIONAIS. Maquinas Virtuais e Emuladores

Introdução Dalvik Linux 2.6. Android. Diogo de Campos, João Paulo Pizani Flor, Maurício Oliveira Haensch, Pedro Covolan Bachiega

Sistemas Operacionais

A Linguagem Algorítmica Estrutura de Repetição. Ex. 2

FBV - Linguagem de Programação II. Um pouco sobre Java

Desenvolvimento Web TCC Turma A-1

SISTEMAS OPERACIONAIS. Apostila 03 Estrutura do Sistema Operacional UNIBAN

AULA Uma linguagem de programação orientada a objetos

Hardware (Nível 0) Organização. Interface de Máquina (IM) Interface Interna de Microprogramação (IIMP)

Programação Orientada a Objetos

Sistemas Operacionais 2014 Introdução. Alexandre Augusto Giron

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira

PROGRAMAÇÃO ORIENTADA A OBJETOS EM JAVA*

Programação de Computadores II TCC Turma A-1

Capítulo 8. Software de Sistema

Programação Orientada a Objetos

MAGREGISTER 1.0: GERADOR DE INTERFACES DE COLETAS DE DADOS PARA PDA S. Acadêmico: Gilson Chequeto Orientador: Adilson Vahldick

Notas da Aula 15 - Fundamentos de Sistemas Operacionais

Universidade da Beira Interior Cursos: Engenharia Informática, Matemática /Informática e Ensino da Informática

Sistemas Operacionais. Conceitos de um Sistema Operacional

Desenvolvimento de um Simulador de Gerenciamento de Memória

Sistemas Operacionais 1/66


Organização e Arquitetura de Computadores I. de Computadores

Programação de Computadores - I. Profª Beatriz Profº Israel

3/9/2010. Ligação da UCP com o barramento do. sistema. As funções básicas dos registradores nos permitem classificá-los em duas categorias:

Figura 1 - O computador

Sistemas Operacionais

Sistemas Operacionais. Roteiro. Sistemas de Computadores. Os sistemas de computadores são projetados com basicamente 3 componentes: Marcos Laureano

Java. Marcio de Carvalho Victorino

UM FRAMEWORK PARA DESENVOLVIMENTO DE

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 2. Cursos de Computação

Sistemas Operacionais

BRAlarmExpert. Software para Gerenciamento de Alarmes. BENEFÍCIOS obtidos com a utilização do BRAlarmExpert:

Sistemas Operacionais

SISTEMAS OPERACIONAIS

Introdução aos Computadores

LP II Estrutura de Dados. Introdução e Linguagem C. Prof. José Honorato F. Nunes honorato.nunes@ifbaiano.bonfim.edu.br

Sistemas Operacionais. Prof. Pedro Luís Antonelli Anhanguera Educacional

Análises Geração RI (representação intermediária) Código Intermediário

Orientação a Objetos

UNIVERSIDADE FEDERAL DE SANTA CATARINA UFSC DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA INE BACHARELADO EM CIÊNCIAS DA COMPUTAÇÃO.

Sistemas Operacionais

ARQUITECTURA DE COMPUTADORES CAPÍTULO II AULA X

Aplicações. Sistema Operacional Hardware. Os sistemas de computadores são projetados com basicamente 3 componentes: Máquinas Virtuais e Emuladores

Um Driver NDIS Para Interceptação de Datagramas IP

Notas da Aula 17 - Fundamentos de Sistemas Operacionais

Modelo para Documento de. Especificação de Requisitos de Software

Modelo para Documento de. Especificação de Requisitos de Software

UFG - Instituto de Informática

Organização e Arquitetura de Computadores

Java Laboratório Aula 1. Divisões da Plataforma. Introdução a Plataforma Java. Visão geral da arquitetura da

Tabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008

Conceitos de Linguagens de Programação

Engenharia de Requisitos

Sistemas Operacionais Aula 03: Estruturas dos SOs. Ezequiel R. Zorzal

NOTAS DE AULA Prof. Antonio Carlos Schneider Beck Filho (UFSM) Prof. Júlio Carlos Balzano de Mattos (UFPel) Arquitetura de Von Neumann

Software Básico (INF1018)

Adriano Reine Bueno Rafael Barros Silva

Sistemas Distribuídos

Visão Geral de Sistemas Operacionais

Capítulo 1. Introdução. 1.1 Linguagens. OBJETIVOS DO CAPÍTULO Ao final deste capítulo você deverá ser capaz de:

Hardware & Software. SOS Digital: Tópico 2

Conceitos de Banco de Dados

E/S PROGRAMADA E/S PROGRAMADA E/S USANDO INTERRUPÇÃO

Linguagem de Programação JAVA. Professora Michelle Nery Nomeclaturas

Introdução à Ciência da Computação

Arquitetura de Computadores. Sistemas Operacionais IV

GERENCIAMENTO DE DISPOSITIVOS

Resumo até aqui. Gerenciamento Proteção Compartilhamento. Infra-estrutura de Software

PROJETO LÓGICO DE COMPUTADORES Prof. Ricardo Rodrigues Barcelar

Geração de código. Ivan Ricarte INTRODUÇÃO À COMPILAÇÃO

Introdução aos Sistemas

3 Um Framework Orientado a Aspectos para Monitoramento e Análise de Processos de Negócio

Marco Aurélio Uma Visão Geral Sobre Plataforma Java

Sistemas Distribuídos: Conceitos e Projeto Threads e Migração de Processos

Figura 01 Kernel de um Sistema Operacional

implementação Nuno Ferreira Neves Faculdade de Ciências de Universidade de Lisboa Fernando Ramos, Nuno Neves, Sistemas Operativos,

Sistemas Operacionais

Laboratório de Computação VI JAVA IDL. Fabricio Aparecido Breve

Arquiteturas RISC. (Reduced Instructions Set Computers)

Para construção dos modelos físicos, será estudado o modelo Relacional como originalmente proposto por Codd.

Sistema Operacional Correção - Exercício de Revisão

Virtualização Gerencia de Redes Redes de Computadores II

ISO/IEC 12207: Gerência de Configuração

Conjunto de instruções do CPU. Arquitectura de um computador. Definição das instruções (1) Definição das instruções (2)

Funções de um SO. Gerência de processos Gerência de memória Gerência de Arquivos Gerência de I/O Sistema de Proteção

Sistemas Operacionais. Prof. M.Sc. Sérgio Teixeira. Aula 05 Estrutura e arquitetura do SO Parte 1. Cursos de Computação

Acadêmicos: Luís Fernando Martins Nagata Gustavo Rezende Vinícius Rezende Santos

Transcrição:

JingleOS: An operating system to embedded devices with language-based protection Luiz Eugênio Fernandes Tenório Centro de Informática (CIn) Universidade Federal de Pernambuco (UFPE) Av. Jornalista Anibal Fernandes, s/n - Cidade Universitária. 50.740-560 - Recife - PE - Brasil left@cin.ufpe.br left@jingleos.org Silvio Romero de Lemos Meira Centro de Informática (CIn) Universidade Federal de Pernambuco (UFPE) Av. Jornalista Aníbal Fernandes, s/n - Cidade Universitária. 50.740-560 - Recife - PE - Brasil srlm@cin.ufpe.br Abstract Language based protection and high-level language virtual machines (JVM, CLR) have solved many problems of portability and dependability. Development of operating systems with these characteristics for embedded systems could enjoy these benefits with the solution of basic problems related to resource consumption and performance. This paper presents the JingleOS, an operating system designed on these concepts for devices with a few kibibytes of RAM and 8 bits microcontrollers. To support the system design, advanced compiler techniques and extensions of the Java programming language were used, in order to allow low-level hardware access and bare metal execution. Resumo Proteção baseada em linguagens e máquinas virtuais para linguagens de alto nível (JVM, CLR) tem solucionado diversos problemas de portabilidade e confiabilidade. O desenvolvimento de sistemas operacionais com estas características para sistemas embarcados poderiam usufruir destes benefícios com a solução de alguns problemas básicos relacionados a consumo de recursos e desempenho. Este artigo apresenta o JingleOS, um sistema operacional projetado sobre estes conceitos para dispositivos com poucos kibibytes de RAM e microcontroladores de 8 bits. Para suportar o projeto do sistema, foram utilizadas técnicas avançadas de compilação e extensões da linguagem de programação Java a fim de permitir acesso e execução direta sobre o hardware. I. INTRODUÇÃO Sistemas desenvolvidos para execução na Internet das Coisas sofrem maiores restrições de recursos devido ao tamanho reduzido dos dispositivos [1]. Sendo os recursos limitados, o desenvolvimento tem como base decisões e otimizações que podem comprometer a confiabilidade na presença de defeitos escapados. A manipulação incorreta do hardware e uso de ponteiros inválidos são causas comuns destes defeitos. Uma solução conhecida é o uso de máquinas virtuais para linguagens de alto nível (ex. Java Virtual Machine - JVM, Common Language Runtime CLR). Este tipo de máquina virtual tem capacidade de controle e contenção através da abstração de acesso direto ao hardware, gerenciamento automático de memória e suporte a proteção baseada em linguagens. Devido à quantidade limitada de recursos de processamento e armazenamento nos dispositivos, estas soluções normalmente são consideradas inadequadas principalmente por causar impacto no desempenho geral do sistema e ter um consumo excessivo de memória. Este artigo apresenta uma solução para utilização de máquinas virtuais nestes dispositivos através de um método alternativo de mapeamento entre o hardware e elementos básicos da linguagem de programação combinado a compilação estática (ahead-of-time) do sistema. O mapeamento para o hardware acontece através de metadados nos elementos da linguagem (atributos e classes), permitindo o acesso ao hardware através de alteração de variáveis ou vetores, enquanto que a compilação adiantada evita o impacto no desempenho. O estudo de caso apresenta um sistema operacional baseado em uma máquina virtual metacircular que foi desenvolvida para execução em um microcontrolador ATmega328 (16MHz) com 2KiB de memória SRAM e 32KiB de memória flash e execução em processadores compatíveis com x86, como o Intel Atom. Além da máquina virtual, um compilador específico efetua a tradução antecipada entre instruções virtuais e nativas, e otimização para alocação simples de registradores com o intuito de minimizar o impacto da arquitetura baseada em pilha na máquina virtual. II. MOTIVAÇÃO Sistemas embarcados tem uma dependência intrínseca do hardware [2]. O desenvolvimento destes sistemas deve considerar esta necessidade durante a definição da plataforma e da linguagem de programação. Diante deste cenário, linguagens de montagem e a linguagem C são escolhas usuais por permitir uma sintonia maior nos recursos utilizados, fato este que permite reduzir a necessidade de recursos para armazenamento e processamento. Contudo, estas linguagens não tem objetivo de abstrair o hardware, permitindo a um desenvolvedor realizar otimizações que, algumas vezes não propositalmente, causam falhas [3]. A utilização de máquinas virtuais para linguagens de alto nível se consolidou no desenvolvimento de aplicações para dispositivos móveis, desktop e Web após as especificações da Java Virtual Machine (JVM) [4] [5] e Common Language Infrastructure (CLI) [30]. Máquinas virtuais definem

abstrações do ambiente de execução para permitir a portabilidade sem recompilação e fornece gerenciamento automático de memória, reduzindo a necessidade de manipulação de ponteiros e as ocorrências de referências nulas, conhecidos causadores de falhas [6]. As abstrações fornecidas por estas máquinas virtuais necessitam de mais recursos [7] e, sendo a execução virtualizada, podem reduzir o desempenho geral do sistema. Além disso, não foram projetadas para execução direta no hardware, necessitando de um sistema operacional e bibliotecas. Para alcançar portabilidade, estas máquinas virtuais [8] definem instruções intermediárias que são interpretadas em tempo de execução, evitando assim, a dependência de um determinado conjunto de instruções nativas. O compromisso com a portabilidade têm o efeito colateral de diminuição do desempenho e da eficiência em um sistema interpretado. A compilação dinâmica (just-in-time) [9] minimiza esta perda através da tradução da instruções intermediárias para instruções nativas. Contudo, mesmo utilizando estes métodos de compilação, as máquinas virtuais ainda são consideradas inadequadas para o contexto de sistemas embarcados, principalmente em plataformas de hardware com poucos kibibytes de memória e microcontroladores 8 ou 16 bits [10]. Em uma avaliação dos principais problemas, estes podem ser caracterizados em: A. Problema 1: Minimizar o uso de recursos em tempo de execução Máquinas virtuais necessitam de estruturas adicionais na memória para fornecer suporte as abstrações de uma linguagem orientada a objetos e ao gerenciamento automático da memória. Cada objeto criado necessita de um cabeçalho para indicar o seu tipo, monitores associados ao objeto, um código hash, campos para o coletor de lixo e tamanho de um vetor (array). Considerando o tamanho médio de 36 bytes por objeto somado ao cabeçalho de 12 bytes [11], um microcontrolador com 2 kibibytes de SRAM poderia armazenar aproximadamente 43 objetos. Isto representa um custo adicional de 516 bytes (25% da SRAM) apenas para armazenar dados sobre os objetos. Além dos cabeçalhos, a máquina virtual necessita de objetos adicionais (metaobjetos) para descrever as estruturas das classes. Estas estruturas contém a localização dos atributos de um objeto, com seu respectivo tipo, e a tabela de métodos virtuais para suportar ligação dinâmica. Quando um objeto é instanciado, deve existir pelo menos outro objeto descrevendo sua classe e uma tabela de métodos, sendo este o custo adicional intrínseco do suporte a orientação a objetos na máquina virtual. Aplicações Java ou.net utilizam uma tabela de símbolos nas classes compiladas para realizar referências a constantes indispensáveis no suporte a ligação dinâmica. Como exemplo, após a compilação, as instruções geradas para invocação de um método tem referência para uma estrutura contendo o nome da classe, nome do método e sua a descrição. Isto permite à máquina virtual efetuar as ligações entre o chamador e chamado tardiamente em tempo de execução [14]. Estas constantes, armazenadas em uma área denominada constant pool, ocupam mais de 50% do espaço de uma classe após a compilação [31] e aumentam consideravelmente o footprint das aplicações. B. Problema 2: Execução de código intermediário na máquina virtual ou tradução para código nativo Para permitir a portabilidade entre diferentes plataformas, as aplicações são compiladas em código intermediário. Desta maneira a máquina virtual deve interpretar ou traduzir em tempo de execução código da aplicação para código nativo. Máquinas virtuais modernas utilizam uma estratégia híbrida [12]. Os métodos são interpretados até alcançar certa quantidade de execuções, o que justifica o custo da tradução dinâmica, evitando o consume de recursos na compilação de métodos pouco utilizados. As estratégias acima devem ser reavaliadas no contexto de microcontroladores. A interpretação requer mais processamento e resulta em menor desempenho se comparado à execução nativa enquanto que a tradução dinâmica requer execução de código gerado em tempo de execução. Contudo, a arquitetura Harvard [32] não permite a execução de código na SRAM. Uma solução é escrever o resultado da compilação dinâmica na memória EEPROM, todavia o acesso a esta memória é mais lento e tem impacto significativo no tempo de compilação [33]. C. Problema 3: Acesso ao hardware sem comprometer as abstrações da máquina virtual Considerando que uma aplicação embarcada tem a maioria de suas funcionalidades dependentes do hardware, o mecanismo de acesso deve considerar esta premissa e realizar o controle suficiente para minimizar o impacto no estado da máquina virtual na ocorrência de falhas. A aplicação executada sobre a máquina virtual necessita de suporte para comunicação com dispositivos, execução de operações nativas não mapeadas nas instruções intermediárias e o tratamento de interrupções. Com a execução de instruções nativas, uma aplicação pode realizar desvios de execução, manipular a pilha de operadores e a memória alocada para a máquina virtual. Isto permite a alteração indiscriminada do estado da máquina virtual, comprometendo as abstrações fornecidas e verificações. Uma alternativa para permitir este acesso sem comprometer completamente a máquina virtual é a utilização de métodos nativos [13], ou seja, funções de uma biblioteca nativa mapeadas em métodos da aplicação. Ao invocar um destes métodos, a máquina virtual cria uma nova pilha para execução da função em questão. Isto evita alterações diretamente na pilha da máquina virtual, contudo, ainda permite o acesso a qualquer região da memória, incluindo as estruturas internas da máquina virtual através da manipulação direta de ponteiros. De qualquer forma, a criação de uma pilha adicional é impeditivo em um sistema com poucos kibibytes de memória, o que descarta esta alternativa.

III. SISTEMA OPERACIONAL JINGLEOS JingleOS é um sistema operacional orientado a objetos com proteção baseada em linguagem para sistemas embarcados, composto por uma máquina virtual metacircular e extensões da linguagem Java. A máquina virtual foi projetada seguindo a especificação da máquina virtual Java 7 [14] e restrições definidas no CLDC 1.0 [15]. Está sendo desenvolvida inteiramente em Java para execução de aplicações Java. Um compilador estático (aheadof-time) traduz o código intermediário da máquina virtual para código de montagem. Aplicações e módulos do sistema operacional seguem a mesma estratégia de compilação. O código resultante da tradução é ligado através do GNU LD [16]. O compilador estático, denominado jinglec, foi desenvolvido especificamente para o sistema, utilizando a arquitetura clássica de compiladores [17] para permitir a adoção de novas plataformas através da especialização de uma classe para geração de código nativo. A estratégia para tradução do código intermediário pode ser customizada, independente do gerador de código nativo. O compilador também foi desenvolvido na linguagem Java, sendo isto necessário para utilização do mesmo código na compilação dinâmica e estática. Ao utilizar código intermediário, o sistema pode ser portado para uma nova plataforma através de recompilação, necessitando apenas do mapeamento de elementos (atributos e métodos) da linguagem para o novo hardware. Metadados [18] indicam os recursos de hardware que devem ser mapeados e o compilador emite o código necessário para acesso a estes recursos. Nas próximas secções serão apresentadas as estratégias definidas no projeto do JingleOS para solucionar os problemas citados. A. Solução 1: Minimizar o uso de recursos em tempo de execução Para reduzir o custo de cabeçalho, os objetos do JingleOS utilizam atributos da classe java.lang.object para armazenar as informações sobre o objeto. Não existe nenhum cabeçalho implícito da máquina virtual. O mesmo mecanismo utilizado para acesso a um atributo (instruções getfield, setfield [14]) é utilizado para obter informações do cabeçalho, o que evita a existência de código duplicado para ler um atributo e manipular o cabeçalho do objeto. Um efeito positivo da representação explícita do cabeçalho do objeto é a facilidade para definição de novos modelos. Para definir um novo modelo, é necessária apenas a criação de uma nova classe java.lang.object com os atributos representando os campos desejados no cabeçalho do objeto. Alguns objetos são imutáveis, ou seja, suas classes não permitem alteração do estado do objeto. Nestes casos, o compilador armazena estes objetos na área de código (EEPROM), economizando assim a memória SRAM. Além dos objetos, também são armazenadas constantes das classes compiladas. A máquina virtual necessita de alguns objetos para executar uma aplicação. Objetos que realizam o gerenciamento de memória, threads e carga de classes devem existir para fornecer a uma aplicação estes serviços básicos. Considerando que estes objetos sempre serão instanciados ao iniciar a máquina virtual, o compilador realiza uma otimização e serializa estes objetos, armazenando na área de dados do executável. Quando a máquina virtual é iniciada, não existe nenhum custo para instanciar estes objetos, pois uma imagem deles já existe na memória. Para economizar na quantidade de objetos na memória heap, instâncias de java.lang.string não são representadas como objetos. Vetores de bytes localizados na área de código contêm os bytes dos caracteres e, durante a compilação, qualquer invocação de método em uma instância será convertida em um acesso a esta área de memória. A biblioteca Java adotada é uma versão reduzida da CLDC 1.0. Esta biblioteca contém o mínimo necessário para execução de uma aplicação Java (classes dos pacotes java.lang e java.io), contudo pode ser estendida ou substituída por uma versão mais completa. Outra biblioteca, específica do JingleOS, contém as anotações e classes de suporte a instruções Java que não podem ser traduzidas diretamente para instruções nativas. Durante a compilação, o constant pool das classes são analisados para permitir a criação eficiente do runtime constant pool com quantidade mínima de memória e sem redundâncias. Literais numéricos são utilizados diretamente quando o código nativo é gerado a partir de instruções Java. Logo, constantes dos tipos Integer 1, Float, Long e Double [14] não são necessárias em tempo de execução. Outras constantes são evitadas com ligações resolvidas na compilação estática. O JingleOS utiliza metaobjetos para descrição de classes, atributos e métodos através de instâncias das classes Field, Klass e Method, presentes no pacote org.jingleos.core.runtime. Estas instâncias são criadas após a leitura dos arquivos de classe (.class) e serializadas para a área de dados do executável na compilação estática. Quando uma constante de tipo Class, Fieldref, Methodref, InterfaceMethodref [14] é encontrada, a referência simbólica é transformada em uma referência para um metaobjeto. A classe java.lang.string, por exemplo, é descrita através de uma instância de Klass e a ligação com a sua superclasse é representada como uma referência a instância de Klass que descreve java.lang.object. Para as constantes do tipo String, a referência do vetor de caracteres será mantido no runtime constant pool. Os caracteres são obtidos da entrada do constant pool apontada no campo string_index da estrutura CONSTANT_String_info [14]. No caso da mesma string ser utilizada por mais de uma classe, apenas uma referência será mantida. Ao final da compilação, as constantes do tipo Utf8 não precisam ser mantidas pois seu conteúdo foi copiado para instâncias de java.lang.string referenciadas nos metaobjetos ou 1 O prefixo CONSTANT_ foi omitido da descrição das constantes para facilitar a leitura.

no runtime constant pool. Constantes do tipo NameAndType são resolvidas ao gerar código a partir de instruções Java e, consequentemente, não precisam ser mantidas. Como o CLDC 1.0 não contempla a invocação dinâmica, constantes do tipo MethodHandle, MethodType e InvokeDynamic não são suportadas. Após a análise do constant pool de todas as classes, as constantes restantes estão referenciadas em um único vetor com elementos do tipo java.lang.object. Como o vetor de constantes não é alterado em tempo de execução, o vetor e objetos referenciados são armazenados na EEPROM, deixando a memória SRAM para objetos criados pela aplicação. Ao compilar estaticamente a classe java.lang.class, o compilador faz referência ao vetor de constantes em um atributo estático desta classe. Desta maneira, instruções e classes do JingleOS tem acesso as constantes em tempo de execução. B. Solução 2: Execução de código intermediário na máquina virtual ou tradução para código nativo O compilador jinglec foi desenvolvido em Java para permitir reuso de código para compilação dinâmica e estática. Durante o projeto do compilador, foram definidas interfaces para permitir adicionar otimizações de acordo com o ambiente de execução. No caso de uma compilação estática, uma classe realizando a interface BytecodeHandler pode executar otimizações que demandam mais recursos [19] e compilação multipasso, enquanto que na compilação dinâmica, outra classe realiza a mesma interface e utiliza uma estratégia de mapeamento direto entre as instruções intermediárias e nativas. Estes cenários demonstram a flexibilidade na estrutura do compilador para permitir a alteração na estratégia de compilação com apenas a substituição de nova classe. A diferença, neste caso, vai ser a escolha da biblioteca de compilação, durante a compilação do próprio compilador. Para complementar a customização das otimizações, existe a independência da plataforma alvo. As interfaces CodeEmitter e DataEmitter definem o contrato para geradores de código e dados. Classes realizando estas interfaces fornecem serviços para os tratadores de instruções intermediárias. A definição de uma nova plataforma acontece através da realização destas interfaces. Na versão atual, o compilador tem suporte para as arquiteturas x86 (Intel Atom) e Atmel AVR (ATmega328). C. Solução 3: Acesso ao hardware sem comprometer as abstrações da máquina virtual Através do mapeamento entre os recursos de hardware e elementos da linguagem de programação (atributos e classes), uma aplicação pode ter acesso a um determinado recurso, sem violar a semântica definida na linguagem Java. Um vetor pode ser mapeado explicitamente em uma região de memória vinculada a um dispositivo, permitindo a aplicação se comunicar com este dispositivo através de I/O mapeado em memória. Entretanto, qualquer tentativa de acesso a um vetor em Java resulta em uma verificação de limites neste vetor. Quando uma aplicação realiza um acesso a uma posição menor que zero ou maior do que o tamanho definido para o vetor, uma exceção é lançada. Considerando que o acesso a memória acontece através de um vetor, a aplicação não poderá realizar acesso utilizando um índice inválido, evitando acessos indevidos que resultariam na escrita ou leitura de áreas indevidas. O mapeamento acontece através de metadados (anotações) nos atributos e métodos de uma classe. Ao encontrar instruções intermediárias que utilizam elementos anotados, o compilador emite um código específico ao invés de traduzir as instruções. O código emitido é apenas um mecanismo de acesso aos recursos de hardware (registradores e memória), não realizando nenhum controle de acesso quando o recurso é compartilhado por mais de uma classe. Esta premissa mantém as características existentes dos elementos da linguagem e permite a customização das políticas de acesso de acordo com as necessidades da aplicação. O desenvolvedor deve providenciar os controles necessários (visibilidade restrita, blocos sincronizados) de acordo com sua necessidade para evitar inconsistências no uso destes recursos. Ao realizar o mapeamento direto entre os elementos da linguagem e os recursos de hardware, a portabilidade da aplicação pode ser comprometida. A utilização dos mapeamentos é recomendada apenas nas classes da plataforma (API do Java e JingleOS), estratégia essa comum em outras máquinas virtuais [13][21]. Sendo assim, o esforço de portabilidade está concentrado na implementação destas APIs para outra arquitetura, onde os recursos de hardware são isolados das aplicações através da utilização de alguns padrões de projeto [34]. Na versão corrente, o JingleOS suporta os seguintes metadados: 1) @Register(name= <r> ): Realiza o mapeamento entre um atributo de classe e um registrador <r> do microcontrolador. Somente os tipos primitivos inteiros byte, short e int são suportados. O registrador mapeado deve ter o mesmo tamanho do tipo. Em um microcontrolador Atmel AVR, os registradores R0-R31 podem ser mapeados em atributos do tipo byte e os registradores R26-R31, utilizados para endereçamento indireto, podem ser mapeados no tipo short. O tipo int (32 bits) não é suportado. No caso do Atom, atributos podem ser mapeados em qualquer registrador (ex. AH, AX, EAX) (Figure 1). Ao encontrar instruções getstatic e setstatic com referência para um atributo anotado, o código nativo gerado utiliza o registrador informado, ao invés de acessar os atributos reais do objeto (a área de memória para estes atributos não é considerada no objeto quando esta anotação é utilizada). Anotações em atributos de instância não são permitidas e as restrições de visibilidade seguem o especificado na linguagem. Figura 1. Registrador ponteiro de pilha mapeado em um atributo.

2) @Address(base= <b>, size= <s> ): Realiza o mapeamento entre atributos de classe e uma região de memória iniciada no endereço <b> e com tamanho opcional <s> (Figure 2). Para utilização com tipos primitivos ou objetos, deve ser informado apenas o endereço. Qualquer tipo é suportado. No caso de vetores, o tamanho deve ser informado para permitir a verificação de limites como especificado nas instruções <x>aload e <x>astore. Os elemento do vetor também podem ser de qualquer tipo. Figura 2. Buffer de vídeo (Intel Atom em modo real) mapeado em um vetor sendo utilizado em um método para limpar o console. 3) @Code(value= <ins> <{op}> ): Esta anotação indica o mapeamento entre um método de classe nativo e uma instrução nativa <ins> em linguagem de montagem (Figura 3). As marcações {op} serão substituídas por parâmetros do método. Apenas tipos primitivos inteiros são suportados como parâmetro. Ao encontrar este meta-dado em um método invocado (instrução intermediária invokestatic), o compilador emite um pop na pilha de operandos para cada parâmetro, na ordem indicada no método, e emite a instrução indicada com os parâmetros. As únicas validações realizadas na invocação destes métodos são dos tipos de parâmetro. Por não ter nenhum controle sobre a instrução indicada, o uso desta anotação é recomendado apenas para I/O, interrupções e instruções nativas sem correspondente no código intermediário da máquina virtual. Figura 3. Métodos nativos mapeados nas instruções in e out (Atmel AVR). O método in utiliza a notação {ret} para indicar que o conteúdo do registrador deverá ser o retorno do método. 4) @Interrupt(value= <n> ): Anotação utilizada para indicar que um determinado método de classe deve ser preparado e registrado como um tratador de interrupção. Ao encontrar esta anotação, o compilador deve emitir código para armazenar e restaurar o contexto, antes e depois da execução do método. Além disso, deve ser emitido código para registrar o método como tratador da interrupção indicada <n>. IV. TRABALHOS RELACIONADOS Existem outras máquinas virtuais para sistemas embarcados, conforme apresentado nesta seção. Contudo, no geral, os requisitos mínimos para execução estão acima do esperado para dispositivos na Internet das Coisas [1]. Além disso, os sistemas operacionais com proteção baseada em linguagem têm sistemas desktop ou dispositivos móveis como alvo. A K Virtual Machine (KVM) [20] é uma máquina virtual da Oracle projetada para dispositivos limitados e utilizada principalmente em telefones celulares. Desenvolvida na linguagem C, esta máquina virtual é a implementação de referencia da especificação CLDC [15]. Tem como requisitos mínimos para execução 40KiB de memória RAM e processadores 16 bits. As aplicações (denominadas MIDlets) são executadas através de um interpretador. Na versão 1.0, não existe uma interface definida para acesso ao código nativo. Bibliotecas nativas precisavam utilizar estruturas internas da máquina virtual. Em versões posteriores (1.1 e 1.1.1), uma interface denominada K Native Interface (KNI) [20] foi definida para permitir a interação entre bibliotecas nativas e a KVM. Para contornar as limitações de desempenho da KVM, uma nova máquina virtual denominada CLDC HotSpot [21] foi projetada. As especificações mínimas para execução consideraram os celulares mais recentes da época, com memória RAM entre 1-4MiB e processadores ARM 32 bits, incluindo a extensão Jazelle [22] que realiza execução das instruções Java. Com um hardware não tão limitado, a CLDC HotSpot utiliza técnicas de compilação adaptativa e dinâmica. O acesso a bibliotecas nativas continua utilizando KNI, assim como na KVM. JavaCard [23] é uma especificação de máquinas virtuais para dispositivos com memória RAM na ordem de 1.2KiB e 16KiB de EEPROM ou Flash. Projetada para utilização em cartões inteligentes, executa aplicações através de interpretação de instruções específicas. Aplicações, denominadas applets, necessitam conversão para um formato CAP (Converted APplet) antes da execução. Por questões de segurança, não existe uma interface para permitir acesso ao hardware. Squawk [24] foi projetada para uso em redes sensores sem fio, mais especificamente no dispositivo Sun SPOT. Também foi desenvolvida em Java e conta com um módulo para tradução de código intermediário para C. O núcleo da máquina virtual requer 80KiB de RAM e também suporta execução interpretada. Uma biblioteca de I/O com funções desenvolvidas em C permitem o acesso ao hardware. JamaicaVM [25] e PERC Pico [26] são máquinas virtuais com suporte para sistemas de tempo real, contudo ambas são compatíveis com Java Standard Edition [14] e não permitem o uso em uma plataforma com recursos limitados. A JamaicaVM necessita de um sistema operacional Linux, enquanto que a PERC Pico pode ser executada com um SO ou direto no metal. Singularity [27], JX [28] e JNode [29] são sistemas operacionais desenvolvidos com proteção baseada em linguagem. O primeiro foi desenvolvido em C# enquanto que os outros em Java. Todos tem capacidade para execução em

arquitetura x86 e tem como objetivo sistemas desktop, contudo apenas o JX permite execução em telefones celulares. Compilação estática é utilizada em todos os sistemas e o acesso ao hardware é realizado através de métodos nativos. V. CONCLUSÕES JingleOS é um sistema operacional desenvolvido em Java para sistemas embarcados com poucos kibibytes de memória RAM. A proteção do sistema tem como base a linguagem de programação e acesso ao hardware através de mapeamento entre recursos de hardware e elementos da linguagem. A compilação do sistema acontece estaticamente (ahead-oftime) através de tradução de código intermediário para código nativo, permitindo a portabilidade através da recompilação. O projeto do compilador permite o reuso do código para compilação dinâmica e estática apenas com a especialização de algumas classes. REFERENCES [1] L. Atzori, A. Iera, and G. Morabito. The Internet of Things: A survey. Computer Networks, 54(15): 2787-2805, 2010. [2] T. Noergaard. Embedded Systems Architecture: A Comprehensive Guide for Engineers and Programmers. Newnes, 2005. [3] J. N. Herder, H. Bos, B. Gras, P. Homburg, A. S. Tanenbaum: Fault isolation for device drivers. In Proceedings of the 2009 IEEE/IFIP International Conference on Dependable Systems and Networks (DSN 2009). IEEE Computer Society, Junho 2009. [4] Z. Mednieks, L. Dornin, G. B. Meike, M. Nakamura. Programming Android. O'Reilly Media, 2011. [5] Brett McLaughlin. Building Java Enterprise Applications. O'Reilly Media, 2002. [6] J. N. Herder, H. Bos, B. Gras, P. Homburg, A. S. Tanenbaum. Construction of a Highly Dependable Operating System. In Proceedings of the 6th European Dependable Computing Conference (EDCC-6), pages 3-12. IEEE Computer Society, Outubro 2006. [7] L. R. Clausen, U. P. Schultz, C. Consel, G. Muller. Java bytecode compression for low-end embedded systems. ACM Transactions on Programming Languages and Systems (TOPLAS), 22(3): 471-489, Maio 2000. [8] T. Downing, J. Meyer. Java Virtual Machine. O'Reilly Media, 1997. [9] J. Aycock. A brief history of just-in-time. ACM Computing Surveys (CSUR), 35(2): 97-113, Junho 2003. [10] J. Caska, M. Schoeberl: Java dust: how small can embedded Java be?. Em Proceedings of the 9th International Workshop on Java Technologies for Real-time and Embedded Systems (JTRES '11), páginas 125-129. ACM, Setembro 2011. [11] D. F. Bacon, S. J. Fink, D. Grove. Space- and Time-Efficient Implementation of the Java Object Model. Em Proceedings of the 16th European Conference on Object-Oriented Programming (ECOOP '02), páginas 111-132. Springer-Verlag, 2002. [12] D. Griswold. The Java HotSpot virtual machine architecture. Sun Microsystems Whitepaper, 1998 [13] S. Liang. Java Native Interface: Programmer's Guide and Specification. Prentice Hall, 1999. [14] T. Lindholm, F. Yellin, G. Bracha, A. Buckley. The Java Virtual Machine Specification Java SE 7 Edition, Julho 2011. http://docs.oracle.com/javase/specs/jvms/se7/html/index.html [15] Connected Limited Device Configuration Specification, Sun Microsystems, Março 2003. http://jcp.org/aboutjava/communityprocess/final/jsr139/index.html [16] GNU Binutils. August, 2007. http://www.gnu.org/software/binutils [17] A. V. Aho, M. S. Lam, R. Sethi, J. D. Ullman. Compilers: Principles, Techniques, and Tools. Prentice Hall, 2006 [18] J. Gosling, B. Joy, G. Steele, G. Bracha, A. Buckley. The Java Language Specification Java SE 7 Edition. Fevereiro, 2012. http://docs.oracle.com/javase/specs/jls/se7/html/index.html [19] M. D. Smith, N. Ramsey, G. Holloway. A generalized algorithm for graph-coloring register allocation. Em Proceedings of the ACM SIGPLAN 2004 conference on Programming language design and implementation (PLDI 04), páginas 277-288. ACM, 2004. [20] J2ME Building Blocks for Mobile Devices - Whitepaper on KVM and the Connected, Limited Device Configuration CLDC. Sun Microsystems, Maio 2000. http://java.sun.com/products/cldc/wp/kvmwp.pdf [21] The CLDC HotSpot Implementation Virtual Machine. Sun Microsystems, Fevereiro 2005. http://java.sun.com/products/cldc/wp/cldc_hi_whitepaper.pdf [22] ARM Jazelle DBX. ARM Holdings. http://www.arm.com/products/processors/technologies/jazelle.php [23] Java Card Platform, Version 2.2.2, Sun Microsystems, Março 2006. http://download.oracle.com/otndocs/jcp/java_card_kit-2.2.2-fr-oth-jspec [24] D. Simon, C. Cifuentes. The squawk virtual machine: Java on the bare metal. Em Proceedings of the 20th annual ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '05), páginas 150-151. ACM, Outubro 2005. [25] F. Siebert, A. Walter. Deterministic execution of Java s primitive bytecode operations. Em Proceedings of the Java Virtual Machine Research and Technology Symposium (JVM 01), páginas 141 152. USENIX, 2001. [26] Aonix. Perc pico 1.1 user manual. http://research.aonix.com/jsc/picomanual.4-19-08.pdf. [27] G. C. Hunt, J. R. Larus. Singularity: rethinking the software stack. ACM SIGOPS Operating Systems Review - Systems work at Microsoft Research, 41(2):37-49, Abril 2007. [28] M. Golm, J. Kleinöder, F. Bellosa. Beyond Address Spaces -Flexibility, Performance, Protection, and Resource Management in the Type-Safe JX Operating System. Em Proceedings of the Eighth Workshop on Hot Topics in Operating Systems (HOTOS 01), página 3. IEEE Computer Society, 2001. [29] Jnode.org, JNode: Java New Operating System Design Effort. http://www.jnode.org [30] Common Language Infrastructure (CLI), 6 a Edição, Junho 2012. [31] C. Rippert, A. Courbot, and G. Grimaud. A low-footprint class loading mechanism for embedded Java virtual machines. Em Proceedings of the 3rd international symposium on Principles and practice of programming in Java (PPPJ '04), páginas 75-82. ACM, 2004. [32] ATMEL. ATmega328P datasheet, doc 8161: 8-bit AVR Microcontroller with 4/8/16/32K Bytes In-System Programmable Flash. http://www.atmel.com/images/doc8161.pdf [33] D. Deville, A. Galland, G. Grimaud, S. Jean. Smart Card Operating Systems: Past, Present and Future. Em Proceedings of the 5 th NORDU/USENIX Conference. USENIX, 2003. [34] E. Gamma, R. Helm, R. Jonhson, J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional, 1994.