1995-11-02 15:27:18 +01:00
|
|
|
/* This file is part of the program psim.
|
|
|
|
|
|
|
|
Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef _VM_C_
|
|
|
|
#define _VM_C_
|
|
|
|
|
|
|
|
#ifndef STATIC_INLINE_VM
|
|
|
|
#define STATIC_INLINE_VM STATIC_INLINE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include "basics.h"
|
|
|
|
|
|
|
|
#include "registers.h"
|
|
|
|
|
|
|
|
#include "device_tree.h"
|
1995-11-05 06:40:15 +01:00
|
|
|
#include "corefile.h"
|
1995-11-02 15:27:18 +01:00
|
|
|
|
|
|
|
#include "vm.h"
|
|
|
|
|
|
|
|
#include "interrupts.h"
|
|
|
|
|
|
|
|
#include "mon.h"
|
|
|
|
|
|
|
|
/* OEA vs VEA
|
|
|
|
|
|
|
|
For the VEA model, the VM layer is almost transparent. It's only
|
|
|
|
purpose is to maintain separate core_map's for the instruction
|
|
|
|
and data address spaces. This being so that writes to instruction
|
|
|
|
space or execution of a data space is prevented.
|
|
|
|
|
|
|
|
For the OEA model things are more complex. The reason for separate
|
|
|
|
instruction and data models becomes crucial. The OEA model is
|
|
|
|
built out of three parts. An instruction map, a data map and an
|
|
|
|
underlying structure that provides access to the VM data kept in
|
|
|
|
main memory. */
|
|
|
|
|
|
|
|
|
|
|
|
/* OEA data structures:
|
|
|
|
|
|
|
|
The OEA model maintains internal data structures that shadow the
|
|
|
|
semantics of the various OEA VM registers (BAT, SR, etc). This
|
|
|
|
allows a simple efficient model of the VM to be implemented.
|
|
|
|
|
|
|
|
Consistency between OEA registers and this model's internal data
|
|
|
|
structures is maintained by updating the structures at
|
|
|
|
`synchronization' points. Of particular note is that (at the time
|
|
|
|
of writing) the memory data types for BAT registers are rebuilt
|
|
|
|
when ever the processor moves between problem and system states */
|
|
|
|
|
|
|
|
|
|
|
|
/* Protection table:
|
|
|
|
|
|
|
|
Matrix of processor state, type of access and validity */
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
om_supervisor_state,
|
|
|
|
om_problem_state,
|
|
|
|
nr_om_modes
|
|
|
|
} om_processor_modes;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
om_data_read, om_data_write,
|
|
|
|
om_instruction_read, om_access_any,
|
|
|
|
nr_om_access_types
|
|
|
|
} om_access_types;
|
|
|
|
|
|
|
|
static int om_valid_access[2][4][nr_om_access_types] = {
|
|
|
|
/* read, write, instruction, any */
|
|
|
|
/* K bit == 0 */
|
|
|
|
{ /*r w i a pp */
|
|
|
|
{ 1, 1, 1, 1 }, /* 00 */
|
|
|
|
{ 1, 1, 1, 1 }, /* 01 */
|
|
|
|
{ 1, 1, 1, 1 }, /* 10 */
|
|
|
|
{ 1, 0, 1, 1 }, /* 11 */
|
|
|
|
},
|
|
|
|
/* K bit == 1 or P bit valid */
|
|
|
|
{ /*r w i a pp */
|
|
|
|
{ 0, 0, 0, 0 }, /* 00 */
|
|
|
|
{ 1, 0, 1, 1 }, /* 01 */
|
|
|
|
{ 1, 1, 1, 1 }, /* 10 */
|
|
|
|
{ 1, 0, 1, 1 }, /* 11 */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Bat translation:
|
|
|
|
|
|
|
|
The bat data structure only contains information on valid BAT
|
|
|
|
translations for the current processor mode and type of access. */
|
|
|
|
|
|
|
|
typedef struct _om_bat {
|
|
|
|
unsigned_word block_effective_page_index;
|
|
|
|
unsigned_word block_effective_page_index_mask;
|
|
|
|
unsigned_word block_length_mask;
|
|
|
|
unsigned_word block_real_page_number;
|
|
|
|
int protection_bits;
|
|
|
|
} om_bat;
|
|
|
|
|
|
|
|
enum _nr_om_bat_registers {
|
|
|
|
nr_om_bat_registers = 4
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _om_bats {
|
|
|
|
int nr_valid_bat_registers;
|
|
|
|
om_bat bat[nr_om_bat_registers];
|
|
|
|
} om_bats;
|
|
|
|
|
|
|
|
|
|
|
|
/* Segment TLB:
|
|
|
|
|
|
|
|
In this model the 32 and 64 bit segment tables are treated in very
|
|
|
|
similar ways. The 32bit segment registers are treated as a
|
|
|
|
simplification of the 64bit segment tlb */
|
|
|
|
|
|
|
|
enum _om_segment_tlb_constants {
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
sizeof_segment_table_entry_group = 128,
|
|
|
|
sizeof_segment_table_entry = 16,
|
|
|
|
#endif
|
|
|
|
om_segment_tlb_index_start_bit = 32,
|
|
|
|
om_segment_tlb_index_stop_bit = 35,
|
|
|
|
nr_om_segment_tlb_entries = 16,
|
|
|
|
nr_om_segment_tlb_constants
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _om_segment_tlb_entry {
|
|
|
|
int key[nr_om_modes];
|
|
|
|
om_access_types invalid_access; /* set to instruction if no_execute bit */
|
|
|
|
unsigned_word masked_virtual_segment_id;
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
int is_valid;
|
|
|
|
unsigned_word masked_effective_segment_id;
|
|
|
|
#endif
|
|
|
|
} om_segment_tlb_entry;
|
|
|
|
|
|
|
|
typedef struct _om_segment_tlb {
|
|
|
|
om_segment_tlb_entry entry[nr_om_segment_tlb_entries];
|
|
|
|
} om_segment_tlb;
|
|
|
|
|
|
|
|
|
|
|
|
/* Page TLB:
|
|
|
|
|
|
|
|
This OEA model includes a small direct map Page TLB. The tlb is to
|
|
|
|
cut down on the need for the OEA to perform walks of the page hash
|
|
|
|
table. */
|
|
|
|
|
|
|
|
enum _om_page_tlb_constants {
|
|
|
|
om_page_tlb_index_start_bit = 46,
|
|
|
|
om_page_tlb_index_stop_bit = 51,
|
|
|
|
nr_om_page_tlb_entries = 64,
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
sizeof_pte_group = 128,
|
|
|
|
sizeof_pte = 16,
|
|
|
|
#endif
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 32)
|
|
|
|
sizeof_pte_group = 64,
|
|
|
|
sizeof_pte = 8,
|
|
|
|
#endif
|
|
|
|
nr_om_page_tlb_constants
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _om_page_tlb_entry {
|
|
|
|
int valid;
|
|
|
|
int protection;
|
|
|
|
unsigned_word masked_virtual_segment_id;
|
|
|
|
unsigned_word masked_page;
|
|
|
|
unsigned_word masked_real_page_number;
|
|
|
|
} om_page_tlb_entry;
|
|
|
|
|
|
|
|
typedef struct _om_page_tlb {
|
|
|
|
om_page_tlb_entry entry[nr_om_page_tlb_entries];
|
|
|
|
} om_page_tlb;
|
|
|
|
|
|
|
|
|
|
|
|
/* memory translation:
|
|
|
|
|
|
|
|
OEA memory translation possibly involves BAT, SR, TLB and HTAB
|
|
|
|
information*/
|
|
|
|
|
|
|
|
typedef struct _om_map {
|
|
|
|
|
|
|
|
/* local cache of register values */
|
|
|
|
int is_relocate;
|
|
|
|
int is_problem_state;
|
|
|
|
|
|
|
|
/* block address translation */
|
|
|
|
om_bats *bat_registers;
|
|
|
|
|
|
|
|
/* failing that, translate ea to va using segment tlb */
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
unsigned_word real_address_of_segment_table;
|
|
|
|
#endif
|
|
|
|
om_segment_tlb *segment_tlb;
|
|
|
|
|
|
|
|
/* then va to ra using hashed page table and tlb */
|
|
|
|
unsigned_word real_address_of_page_table;
|
|
|
|
unsigned_word page_table_hash_mask;
|
|
|
|
om_page_tlb *page_tlb;
|
|
|
|
|
|
|
|
/* physical memory for fetching page table entries */
|
|
|
|
core_map *physical;
|
|
|
|
|
|
|
|
} om_map;
|
|
|
|
|
|
|
|
|
|
|
|
/* VM objects:
|
|
|
|
|
|
|
|
External objects defined by vm.h */
|
|
|
|
|
|
|
|
struct _vm_instruction_map {
|
|
|
|
/* real memory for last part */
|
|
|
|
core_map *code;
|
|
|
|
/* translate effective to real */
|
|
|
|
om_map translation;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _vm_data_map {
|
|
|
|
/* translate effective to real */
|
|
|
|
om_map translation;
|
|
|
|
/* real memory for translated address */
|
|
|
|
core_map *read;
|
|
|
|
core_map *write;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* VM:
|
|
|
|
|
|
|
|
Underlying memory object. For the VEA this is just the
|
|
|
|
core_map. For OEA it is the instruction and data memory
|
|
|
|
translation's */
|
|
|
|
|
|
|
|
struct _vm {
|
|
|
|
|
|
|
|
/* OEA: base address registers */
|
|
|
|
om_bats ibats;
|
|
|
|
om_bats dbats;
|
|
|
|
|
|
|
|
/* OEA: segment registers */
|
|
|
|
om_segment_tlb segment_tlb;
|
|
|
|
|
|
|
|
/* OEA: translation lookaside buffers */
|
|
|
|
om_page_tlb instruction_tlb;
|
|
|
|
om_page_tlb data_tlb;
|
|
|
|
|
|
|
|
/* real memory */
|
|
|
|
core *physical;
|
|
|
|
|
|
|
|
/* memory maps */
|
|
|
|
vm_instruction_map instruction_map;
|
|
|
|
vm_data_map data_map;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* OEA Support procedures */
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_VM unsigned_word
|
|
|
|
om_segment_tlb_index(unsigned_word ea)
|
|
|
|
{
|
|
|
|
unsigned_word index = EXTRACTED(ea,
|
|
|
|
om_segment_tlb_index_start_bit,
|
|
|
|
om_segment_tlb_index_stop_bit);
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC_INLINE_VM unsigned_word
|
|
|
|
om_page_tlb_index(unsigned_word ea)
|
|
|
|
{
|
|
|
|
unsigned_word index = EXTRACTED(ea,
|
|
|
|
om_page_tlb_index_start_bit,
|
|
|
|
om_page_tlb_index_stop_bit);
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC_INLINE_VM unsigned_word
|
|
|
|
om_masked_page(unsigned_word ea)
|
|
|
|
{
|
|
|
|
unsigned_word masked_page = MASKED(ea, 36, 51);
|
|
|
|
return masked_page;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC_INLINE_VM unsigned_word
|
|
|
|
om_masked_byte(unsigned_word ea)
|
|
|
|
{
|
|
|
|
unsigned_word masked_byte = MASKED(ea, 52, 63);
|
|
|
|
return masked_byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM vm *
|
|
|
|
vm_create(core *physical)
|
|
|
|
{
|
|
|
|
vm *virtual;
|
|
|
|
|
|
|
|
/* internal checks */
|
|
|
|
if (nr_om_segment_tlb_entries
|
|
|
|
!= (1 << (om_segment_tlb_index_stop_bit
|
|
|
|
- om_segment_tlb_index_start_bit + 1)))
|
|
|
|
error("new_vm() - internal error with om_segment constants\n");
|
|
|
|
if (nr_om_page_tlb_entries
|
|
|
|
!= (1 << (om_page_tlb_index_stop_bit
|
|
|
|
- om_page_tlb_index_start_bit + 1)))
|
|
|
|
error("new_vm() - internal error with om_page constants\n");
|
|
|
|
|
|
|
|
/* create the new vm register file */
|
|
|
|
virtual = ZALLOC(vm);
|
|
|
|
|
|
|
|
/* set up core */
|
|
|
|
virtual->physical = physical;
|
|
|
|
|
|
|
|
/* set up the address decoders */
|
|
|
|
virtual->instruction_map.translation.bat_registers = &virtual->ibats;
|
|
|
|
virtual->instruction_map.translation.segment_tlb = &virtual->segment_tlb;
|
|
|
|
virtual->instruction_map.translation.page_tlb = &virtual->instruction_tlb;
|
|
|
|
virtual->instruction_map.translation.is_relocate = 0;
|
|
|
|
virtual->instruction_map.translation.is_problem_state = 0;
|
|
|
|
virtual->instruction_map.translation.physical = core_readable(physical);
|
|
|
|
virtual->instruction_map.code = core_readable(physical);
|
|
|
|
|
|
|
|
virtual->data_map.translation.bat_registers = &virtual->dbats;
|
|
|
|
virtual->data_map.translation.segment_tlb = &virtual->segment_tlb;
|
|
|
|
virtual->data_map.translation.page_tlb = &virtual->data_tlb;
|
|
|
|
virtual->data_map.translation.is_relocate = 0;
|
|
|
|
virtual->data_map.translation.is_problem_state = 0;
|
|
|
|
virtual->data_map.translation.physical = core_readable(physical);
|
|
|
|
virtual->data_map.read = core_readable(physical);
|
|
|
|
virtual->data_map.write = core_writeable(physical);
|
|
|
|
|
|
|
|
return virtual;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_VM om_bat *
|
|
|
|
om_effective_to_bat(om_map *map,
|
|
|
|
unsigned_word ea)
|
|
|
|
{
|
|
|
|
int curr_bat = 0;
|
|
|
|
om_bats *bats = map->bat_registers;
|
|
|
|
int nr_bats = bats->nr_valid_bat_registers;
|
|
|
|
|
|
|
|
for (curr_bat = 0; curr_bat < nr_bats; curr_bat++) {
|
|
|
|
om_bat *bat = bats->bat + curr_bat;
|
|
|
|
if ((ea & bat->block_effective_page_index_mask)
|
|
|
|
!= bat->block_effective_page_index)
|
|
|
|
continue;
|
|
|
|
return bat;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_VM om_segment_tlb_entry *
|
|
|
|
om_effective_to_virtual(om_map *map,
|
|
|
|
unsigned_word ea,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia)
|
|
|
|
{
|
|
|
|
/* first try the segment tlb */
|
|
|
|
om_segment_tlb_entry *segment_tlb_entry = (map->segment_tlb->entry
|
|
|
|
+ om_segment_tlb_index(ea));
|
|
|
|
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 32)
|
|
|
|
return segment_tlb_entry;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
if (segment_tlb_entry->is_valid
|
|
|
|
&& (segment_tlb_entry->masked_effective_segment_id == MASKED(ea, 0, 35))) {
|
|
|
|
error("fixme - is there a need to update any bits\n");
|
|
|
|
return segment_tlb_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* drats, segment tlb missed */
|
|
|
|
{
|
|
|
|
unsigned_word segment_id_hash = ea;
|
|
|
|
int current_hash = 0;
|
|
|
|
for (current_hash = 0; current_hash < 2; current_hash += 1) {
|
|
|
|
unsigned_word segment_table_entry_group =
|
|
|
|
(map->real_address_of_segment_table
|
|
|
|
| (MASKED64(segment_id_hash, 31, 35) >> (56-35)));
|
|
|
|
unsigned_word segment_table_entry;
|
|
|
|
for (segment_table_entry = segment_table_entry_group;
|
|
|
|
segment_table_entry < (segment_table_entry_group
|
|
|
|
+ sizeof_segment_table_entry_group);
|
|
|
|
segment_table_entry += sizeof_segment_table_entry) {
|
|
|
|
/* byte order? */
|
|
|
|
unsigned_word segment_table_entry_dword_0 =
|
|
|
|
core_map_read_8(map->physical, segment_table_entry, processor, cia);
|
|
|
|
unsigned_word segment_table_entry_dword_1 =
|
|
|
|
core_map_read_8(map->physical, segment_table_entry + 8, processor, cia);
|
|
|
|
int is_valid = MASKED64(segment_table_entry_dword_0, 56, 56) != 0;
|
|
|
|
unsigned_word masked_effective_segment_id =
|
|
|
|
MASKED64(segment_table_entry_dword_0, 0, 35);
|
|
|
|
if (is_valid && masked_effective_segment_id == MASKED64(ea, 0, 35)) {
|
|
|
|
/* don't permit some things */
|
|
|
|
if (MASKED64(segment_table_entry_dword_0, 57, 57))
|
|
|
|
error("om_effective_to_virtual() - T=1 in STE not supported\n");
|
|
|
|
/* update segment tlb */
|
|
|
|
segment_tlb_entry->is_valid = is_valid;
|
|
|
|
segment_tlb_entry->masked_effective_segment_id =
|
|
|
|
masked_effective_segment_id;
|
|
|
|
segment_tlb_entry->key[om_supervisor_state] =
|
|
|
|
EXTRACTED64(segment_table_entry_dword_0, 58, 58);
|
|
|
|
segment_tlb_entry->key[om_problem_state] =
|
|
|
|
EXTRACTED64(segment_table_entry_dword_0, 59, 59);
|
|
|
|
segment_tlb_entry->invalid_access =
|
|
|
|
(MASKED64(segment_table_entry_dword_0, 60, 60)
|
|
|
|
? om_instruction_read
|
|
|
|
: om_access_any);
|
|
|
|
segment_tlb_entry->masked_virtual_segment_id =
|
|
|
|
MASKED(segment_table_entry_dword_1, 0, 51);
|
|
|
|
return segment_tlb_entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
segment_id_hash = ~segment_id_hash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_VM om_page_tlb_entry *
|
|
|
|
om_virtual_to_real(om_map *map,
|
|
|
|
unsigned_word ea,
|
|
|
|
om_segment_tlb_entry *segment_tlb_entry,
|
|
|
|
om_access_types access,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia)
|
|
|
|
{
|
|
|
|
om_page_tlb_entry *page_tlb_entry = (map->page_tlb->entry
|
|
|
|
+ om_page_tlb_index(ea));
|
|
|
|
|
|
|
|
/* is it a tlb hit? */
|
|
|
|
if (page_tlb_entry->valid
|
|
|
|
&& (page_tlb_entry->masked_virtual_segment_id ==
|
|
|
|
segment_tlb_entry->masked_virtual_segment_id)
|
|
|
|
&& (page_tlb_entry->masked_page == om_masked_page(ea))) {
|
|
|
|
error("fixme - it is not a hit if direction/update bits do not match\n");
|
|
|
|
return page_tlb_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* drats, it is a tlb miss */
|
|
|
|
{
|
|
|
|
unsigned_word page_hash = (segment_tlb_entry->masked_virtual_segment_id
|
|
|
|
^ om_masked_page(ea));
|
|
|
|
int current_hash;
|
|
|
|
for (current_hash = 0; current_hash < 2; current_hash += 1) {
|
|
|
|
unsigned_word real_address_of_pte_group =
|
|
|
|
(map->real_address_of_page_table
|
|
|
|
| (page_hash & map->page_table_hash_mask));
|
|
|
|
unsigned_word real_address_of_pte;
|
|
|
|
for (real_address_of_pte = real_address_of_pte_group;
|
|
|
|
real_address_of_pte < (real_address_of_pte_group
|
|
|
|
+ sizeof_pte_group);
|
|
|
|
real_address_of_pte += sizeof_pte) {
|
|
|
|
unsigned_word pte_word_0 =
|
|
|
|
core_map_read_word(map->physical,
|
|
|
|
real_address_of_pte,
|
|
|
|
processor, cia);
|
|
|
|
unsigned_word pte_word_1 =
|
|
|
|
core_map_read_word(map->physical,
|
|
|
|
real_address_of_pte + sizeof_pte / 2,
|
|
|
|
processor, cia);
|
|
|
|
error("fixme - check pte hit\n");
|
|
|
|
if (1) {
|
|
|
|
error("fixme - update the page_tlb\n");
|
|
|
|
page_tlb_entry->valid = 1;
|
|
|
|
page_tlb_entry->protection = 0;
|
|
|
|
page_tlb_entry->masked_virtual_segment_id = 0;
|
|
|
|
page_tlb_entry->masked_page = 0;
|
|
|
|
page_tlb_entry->masked_real_page_number = 0;
|
|
|
|
return page_tlb_entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
page_hash = ~page_hash; /*???*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
om_interrupt(cpu *processor,
|
|
|
|
unsigned_word cia,
|
|
|
|
unsigned_word ea,
|
|
|
|
om_access_types access,
|
|
|
|
storage_interrupt_reasons reason)
|
|
|
|
{
|
|
|
|
switch (access) {
|
|
|
|
case om_data_read:
|
|
|
|
data_storage_interrupt(processor, cia, ea, reason, 0/*!is_store*/);
|
|
|
|
break;
|
|
|
|
case om_data_write:
|
|
|
|
data_storage_interrupt(processor, cia, ea, reason, 1/*is_store*/);
|
|
|
|
break;
|
|
|
|
case om_instruction_read:
|
|
|
|
instruction_storage_interrupt(processor, cia, reason);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("om_interrupt - unexpected access type %d, cia=0x%x, ea=0x%x\n",
|
|
|
|
access, cia, ea);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_VM unsigned_word
|
|
|
|
om_translate_effective_to_real(om_map *map,
|
|
|
|
unsigned_word ea,
|
|
|
|
om_access_types access,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia,
|
|
|
|
int abort)
|
|
|
|
{
|
|
|
|
om_bat *bat = NULL;
|
|
|
|
om_segment_tlb_entry *segment_tlb_entry = NULL;
|
|
|
|
om_page_tlb_entry *page_tlb_entry = NULL;
|
|
|
|
unsigned_word ra;
|
|
|
|
|
|
|
|
if (!map->is_relocate) {
|
|
|
|
ra = ea;
|
|
|
|
TRACE(trace_vm, ("%s, direct map, ea=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea));
|
|
|
|
return ra;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* match with BAT? */
|
|
|
|
bat = om_effective_to_bat(map, ea);
|
|
|
|
if (bat != NULL) {
|
|
|
|
if (!om_valid_access[1][bat->protection_bits][access]) {
|
|
|
|
TRACE(trace_vm, ("%s, bat protection violation, ea=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea));
|
|
|
|
if (abort)
|
|
|
|
om_interrupt(processor, cia, ea, access,
|
|
|
|
protection_violation_storage_interrupt);
|
|
|
|
else
|
|
|
|
return MASK(0, 63);
|
|
|
|
}
|
|
|
|
|
|
|
|
ra = ((ea & bat->block_length_mask) | bat->block_real_page_number);
|
|
|
|
TRACE(trace_vm, ("%s, bat translation, ea=0x%x, ra=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea, ra));
|
|
|
|
return ra;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* translate ea to va using segment map */
|
|
|
|
segment_tlb_entry = om_effective_to_virtual(map, ea, processor, cia);
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
if (segment_tlb_entry == NULL) {
|
|
|
|
TRACE(trace_vm, ("%s, segment tlb lookup failed - ea=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea));
|
|
|
|
if (abort)
|
|
|
|
om_interrupt(processor, cia, ea, access,
|
|
|
|
segment_table_miss_storage_interrupt);
|
|
|
|
else
|
|
|
|
return MASK(0, 63);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* check for invalid segment access type */
|
|
|
|
if (segment_tlb_entry->invalid_access == access) {
|
|
|
|
TRACE(trace_vm, ("%s, segment tlb access invalid - ea=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea));
|
|
|
|
if (abort)
|
|
|
|
om_interrupt(processor, cia, ea, access,
|
|
|
|
protection_violation_storage_interrupt);
|
|
|
|
else
|
|
|
|
return MASK(0, 63);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lookup in PTE */
|
|
|
|
page_tlb_entry = om_virtual_to_real(map, ea, segment_tlb_entry,
|
|
|
|
access,
|
|
|
|
processor, cia);
|
|
|
|
if (page_tlb_entry == NULL) {
|
|
|
|
TRACE(trace_vm, ("%s, page tlb lookup failed - ea=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea));
|
|
|
|
if (abort)
|
|
|
|
om_interrupt(processor, cia, ea, access,
|
|
|
|
hash_table_miss_storage_interrupt);
|
|
|
|
else
|
|
|
|
return MASK(0, 63);
|
|
|
|
}
|
|
|
|
if (!(om_valid_access
|
|
|
|
[segment_tlb_entry->key[map->is_problem_state]]
|
|
|
|
[page_tlb_entry->protection]
|
|
|
|
[access])) {
|
|
|
|
TRACE(trace_vm, ("%s, page tlb access invalid - ea=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea));
|
|
|
|
if (abort)
|
|
|
|
om_interrupt(processor, cia, ea, access,
|
|
|
|
protection_violation_storage_interrupt);
|
|
|
|
else
|
|
|
|
return MASK(0, 63);
|
|
|
|
}
|
|
|
|
|
|
|
|
ra = (page_tlb_entry->masked_real_page_number
|
|
|
|
| om_masked_byte(ea));
|
|
|
|
TRACE(trace_vm, ("%s, page - ea=0x%x, ra=0x%x\n",
|
|
|
|
"om_translate_effective_to_real",
|
|
|
|
ea, ra));
|
|
|
|
return ra;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Definition of operations for memory management
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* rebuild all the relevant bat information */
|
|
|
|
STATIC_INLINE_VM void
|
|
|
|
om_unpack_bat(om_bat *bat,
|
|
|
|
spreg ubat,
|
|
|
|
spreg lbat)
|
|
|
|
{
|
|
|
|
/* for extracting out the offset within a page */
|
|
|
|
bat->block_length_mask = ((MASKED(ubat, 51, 61) << (17-2))
|
|
|
|
| MASK(63-17+1, 63));
|
|
|
|
|
|
|
|
/* for checking the effective page index */
|
|
|
|
bat->block_effective_page_index = MASKED(ubat, 0, 46);
|
|
|
|
bat->block_effective_page_index_mask = ~bat->block_length_mask;
|
|
|
|
|
|
|
|
/* protection information */
|
|
|
|
bat->protection_bits = EXTRACTED(lbat, 62, 63);
|
|
|
|
bat->block_real_page_number = MASKED(lbat, 0, 46);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* rebuild the given bat table */
|
|
|
|
STATIC_INLINE_VM void
|
|
|
|
om_unpack_bats(om_bats *bats,
|
|
|
|
spreg *raw_bats,
|
|
|
|
msreg msr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
bats->nr_valid_bat_registers = 0;
|
|
|
|
for (i = 0; i < nr_om_bat_registers*2; i += 2) {
|
|
|
|
spreg ubat = raw_bats[i];
|
|
|
|
spreg lbat = raw_bats[i+1];
|
|
|
|
if ((msr & msr_problem_state)
|
|
|
|
? EXTRACTED(ubat, 62, 62)
|
|
|
|
: EXTRACTED(ubat, 63, 63)) {
|
|
|
|
om_unpack_bat(&bats->bat[bats->nr_valid_bat_registers],
|
|
|
|
ubat, lbat);
|
|
|
|
bats->nr_valid_bat_registers += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 32)
|
|
|
|
STATIC_INLINE_VM void
|
|
|
|
om_unpack_sr(vm *virtual,
|
|
|
|
sreg *srs,
|
|
|
|
int which_sr)
|
|
|
|
{
|
|
|
|
om_segment_tlb_entry *segment_tlb_entry = 0;
|
|
|
|
sreg new_sr_value = 0;
|
|
|
|
|
|
|
|
/* check register in range */
|
|
|
|
if (which_sr < 0 || which_sr > nr_om_segment_tlb_entries)
|
|
|
|
error("om_set_sr: segment register out of bounds\n");
|
|
|
|
|
|
|
|
/* get the working values */
|
|
|
|
segment_tlb_entry = &virtual->segment_tlb.entry[which_sr];
|
|
|
|
new_sr_value = srs[which_sr];
|
|
|
|
|
|
|
|
/* do we support this */
|
|
|
|
if (MASKED32(new_sr_value, 0, 0))
|
|
|
|
error("om_ser_sr(): unsupported value of T in segment register %d\n",
|
|
|
|
which_sr);
|
|
|
|
|
|
|
|
/* update info */
|
|
|
|
segment_tlb_entry->key[om_supervisor_state] = EXTRACTED32(new_sr_value, 1, 1);
|
|
|
|
segment_tlb_entry->key[om_problem_state] = EXTRACTED32(new_sr_value, 2, 2);
|
|
|
|
segment_tlb_entry->invalid_access = (MASKED32(new_sr_value, 3, 3)
|
|
|
|
? om_instruction_read
|
|
|
|
: om_access_any);
|
|
|
|
segment_tlb_entry->masked_virtual_segment_id = MASKED32(new_sr_value, 8, 31);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 32)
|
|
|
|
STATIC_INLINE_VM void
|
|
|
|
om_unpack_srs(vm *virtual,
|
|
|
|
sreg *srs)
|
|
|
|
{
|
|
|
|
int which_sr;
|
|
|
|
for (which_sr = 0; which_sr < nr_om_segment_tlb_entries; which_sr++) {
|
|
|
|
om_unpack_sr(virtual, srs, which_sr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* Rebuild all the data structures for the new context as specifed by
|
|
|
|
the passed registers */
|
|
|
|
INLINE_VM void
|
|
|
|
vm_synchronize_context(vm *virtual,
|
|
|
|
spreg *sprs,
|
|
|
|
sreg *srs,
|
|
|
|
msreg msr)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* enable/disable translation */
|
|
|
|
int problem_state = (msr & msr_problem_state) != 0;
|
|
|
|
int data_relocate = (msr & msr_data_relocate) != 0;
|
|
|
|
int instruction_relocate = (msr & msr_instruction_relocate) != 0;
|
|
|
|
|
|
|
|
unsigned_word page_table_hash_mask;
|
|
|
|
unsigned_word real_address_of_page_table;
|
|
|
|
|
|
|
|
|
|
|
|
/* update current processor mode */
|
|
|
|
virtual->instruction_map.translation.is_relocate = instruction_relocate;
|
|
|
|
virtual->instruction_map.translation.is_problem_state = problem_state;
|
|
|
|
virtual->data_map.translation.is_relocate = data_relocate;
|
|
|
|
virtual->data_map.translation.is_problem_state = problem_state;
|
|
|
|
|
|
|
|
|
|
|
|
/* update bat registers for the new context */
|
|
|
|
om_unpack_bats(&virtual->ibats, &sprs[spr_ibat0u], msr);
|
|
|
|
om_unpack_bats(&virtual->dbats, &sprs[spr_dbat0u], msr);
|
|
|
|
|
|
|
|
|
|
|
|
/* unpack SDR1 - the storage description register 1 */
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 64)
|
|
|
|
real_address_of_page_table = EXTRACTED64(sprs[spr_sdr1], 0, 45);
|
|
|
|
page_table_hash_mask = MASK64(47-EXTRACTED64(sprs[spr_sdr1], 59, 63),
|
|
|
|
57);
|
|
|
|
#endif
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 32)
|
|
|
|
real_address_of_page_table = EXTRACTED32(sprs[spr_sdr1], 0, 15);
|
|
|
|
page_table_hash_mask = ((EXTRACTED32(sprs[spr_sdr1], 23, 31) << (10+6))
|
|
|
|
| MASK32(16, 25));
|
|
|
|
#endif
|
|
|
|
virtual->instruction_map.translation.real_address_of_page_table = real_address_of_page_table;
|
|
|
|
virtual->instruction_map.translation.page_table_hash_mask = page_table_hash_mask;
|
|
|
|
virtual->data_map.translation.real_address_of_page_table = real_address_of_page_table;
|
|
|
|
virtual->data_map.translation.page_table_hash_mask = page_table_hash_mask;
|
|
|
|
|
|
|
|
|
|
|
|
#if (WITH_TARGET_WORD_BITSIZE == 32)
|
|
|
|
/* unpack the segment tlb registers */
|
|
|
|
om_unpack_srs(virtual, srs);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM vm_data_map *
|
|
|
|
vm_create_data_map(vm *memory)
|
|
|
|
{
|
|
|
|
return &memory->data_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM vm_instruction_map *
|
|
|
|
vm_create_instruction_map(vm *memory)
|
|
|
|
{
|
|
|
|
return &memory->instruction_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC_INLINE_VM unsigned_word
|
|
|
|
vm_translate(om_map *map,
|
|
|
|
unsigned_word ea,
|
|
|
|
om_access_types access,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia,
|
|
|
|
int abort)
|
|
|
|
{
|
|
|
|
switch (CURRENT_ENVIRONMENT) {
|
|
|
|
case USER_ENVIRONMENT:
|
|
|
|
case VIRTUAL_ENVIRONMENT:
|
|
|
|
return ea;
|
|
|
|
case OPERATING_ENVIRONMENT:
|
|
|
|
return om_translate_effective_to_real(map, ea, access,
|
|
|
|
processor, cia,
|
|
|
|
abort);
|
|
|
|
default:
|
|
|
|
error("vm_translate() - unknown environment\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM unsigned_word
|
|
|
|
vm_real_data_addr(vm_data_map *map,
|
|
|
|
unsigned_word ea,
|
|
|
|
int is_read,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia)
|
|
|
|
{
|
|
|
|
return vm_translate(&map->translation,
|
|
|
|
ea,
|
|
|
|
is_read ? om_data_read : om_data_write,
|
|
|
|
processor,
|
|
|
|
cia,
|
|
|
|
1); /*abort*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM unsigned_word
|
|
|
|
vm_real_instruction_addr(vm_instruction_map *map,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia)
|
|
|
|
{
|
|
|
|
return vm_translate(&map->translation,
|
|
|
|
cia,
|
|
|
|
om_instruction_read,
|
|
|
|
processor,
|
|
|
|
cia,
|
|
|
|
1); /*abort*/
|
|
|
|
}
|
|
|
|
|
|
|
|
INLINE_VM instruction_word
|
|
|
|
vm_instruction_map_read(vm_instruction_map *map,
|
|
|
|
cpu *processor,
|
|
|
|
unsigned_word cia)
|
|
|
|
{
|
|
|
|
unsigned_word ra = vm_real_instruction_addr(map, processor, cia);
|
|
|
|
ASSERT((cia & 0x3) == 0); /* always aligned */
|
|
|
|
return core_map_read_4(map->code, ra, processor, cia);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM int
|
|
|
|
vm_data_map_read_buffer(vm_data_map *map,
|
|
|
|
void *target,
|
|
|
|
unsigned_word addr,
|
|
|
|
unsigned nr_bytes)
|
|
|
|
{
|
|
|
|
unsigned count;
|
|
|
|
for (count = 0; count < nr_bytes; count++) {
|
|
|
|
unsigned_1 byte;
|
|
|
|
unsigned_word ea = addr + count;
|
|
|
|
unsigned_word ra = vm_translate(&map->translation,
|
|
|
|
ea, om_data_read,
|
|
|
|
NULL, /*processor*/
|
|
|
|
0, /*cia*/
|
|
|
|
0); /*dont-abort*/
|
|
|
|
if (ra == MASK(0, 63))
|
|
|
|
break;
|
|
|
|
if (core_map_read_buffer(map->read, &byte, ea, sizeof(byte))
|
|
|
|
!= sizeof(byte))
|
|
|
|
break;
|
|
|
|
((unsigned_1*)target)[count] = T2H_1(byte);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INLINE_VM int
|
|
|
|
vm_data_map_write_buffer(vm_data_map *map,
|
|
|
|
const void *source,
|
|
|
|
unsigned_word addr,
|
|
|
|
unsigned nr_bytes,
|
|
|
|
int violate_read_only_section)
|
|
|
|
{
|
|
|
|
unsigned count;
|
|
|
|
unsigned_1 byte;
|
|
|
|
for (count = 0; count < nr_bytes; count++) {
|
|
|
|
unsigned_word ea = addr + count;
|
|
|
|
unsigned_word ra = vm_translate(&map->translation,
|
|
|
|
ea, om_data_write,
|
|
|
|
NULL/*processor*/,
|
|
|
|
0, /*cia*/
|
|
|
|
0); /*dont-abort*/
|
|
|
|
if (ra == MASK(0, 63))
|
|
|
|
break;
|
|
|
|
byte = T2H_1(((unsigned_1*)source)[count]);
|
|
|
|
if (core_map_write_buffer((violate_read_only_section
|
|
|
|
? map->read
|
|
|
|
: map->write),
|
|
|
|
&byte, ra, sizeof(byte)) != sizeof(byte))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* define the read/write 1/2/4/8/word functions */
|
|
|
|
|
|
|
|
#undef N
|
|
|
|
#define N 1
|
|
|
|
#include "vm_n.h"
|
|
|
|
|
|
|
|
#undef N
|
|
|
|
#define N 2
|
|
|
|
#include "vm_n.h"
|
|
|
|
|
|
|
|
#undef N
|
|
|
|
#define N 4
|
|
|
|
#include "vm_n.h"
|
|
|
|
|
|
|
|
#undef N
|
|
|
|
#define N 8
|
|
|
|
#include "vm_n.h"
|
|
|
|
|
|
|
|
#undef N
|
|
|
|
#define N word
|
|
|
|
#include "vm_n.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* _VM_C_ */
|